Bug 5260 - Add option to send an order by e-mail to the acquisition module
[koha.git] / acqui / basket.pl
1 #!/usr/bin/perl
2
3 #script to show display basket of orders
4
5 # Copyright 2000 - 2004 Katipo
6 # Copyright 2008 - 2009 BibLibre SARL
7 #
8 # This file is part of Koha.
9 #
10 # Koha is free software; you can redistribute it and/or modify it
11 # under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 3 of the License, or
13 # (at your option) any later version.
14 #
15 # Koha is distributed in the hope that it will be useful, but
16 # WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License
21 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22
23 use Modern::Perl;
24 use C4::Auth;
25 use C4::Koha;
26 use C4::Output;
27 use CGI qw ( -utf8 );
28 use C4::Acquisition;
29 use C4::Budgets;
30 use C4::Contract;
31 use C4::Debug;
32 use C4::Biblio;
33 use C4::Members qw/GetMember/;  #needed for permissions checking for changing basketgroup of a basket
34 use C4::Items;
35 use C4::Suggestions;
36 use Koha::Libraries;
37 use C4::Letters qw/SendAlerts/;
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 $template->param( email_ok => 1 ) if defined $query->param('email_ok');
120 $template->param( email_error => $query->param('email_error') ) if defined $query->param('email_error');
121
122
123 if ( $op eq 'delete_confirm' ) {
124     my $basketno = $query->param('basketno');
125     my $delbiblio = $query->param('delbiblio');
126     my @orders = GetOrders($basketno);
127 #Delete all orders included in that basket, and all items received.
128     foreach my $myorder (@orders){
129         DelOrder($myorder->{biblionumber},$myorder->{ordernumber});
130     }
131 # if $delbiblio = 1, delete the records if possible
132     if ((defined $delbiblio)and ($delbiblio ==1)){
133         my @cannotdelbiblios ;
134         foreach my $myorder (@orders){
135             my $biblionumber = $myorder->{'biblionumber'};
136             my $countbiblio = CountBiblioInOrders($biblionumber);
137             my $ordernumber = $myorder->{'ordernumber'};
138             my $subscriptions = scalar GetSubscriptionsId ($biblionumber);
139             my $itemcount = GetItemsCount($biblionumber);
140             my $error;
141             if ($countbiblio == 0 && $itemcount == 0 && $subscriptions == 0) {
142                 $error = DelBiblio($myorder->{biblionumber}) }
143             else {
144                 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
145                                          title=> $myorder->{'title'},
146                                          author=> $myorder->{'author'},
147                                          countbiblio=> $countbiblio,
148                                          itemcount=>$itemcount,
149                                          subscriptions=>$subscriptions};
150             }
151             if ($error) {
152                 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
153                                          title=> $myorder->{'title'},
154                                          author=> $myorder->{'author'},
155                                          othererror=> $error};
156             }
157         }
158         $template->param( cannotdelbiblios => \@cannotdelbiblios );
159     }
160  # delete the basket
161     DelBasket($basketno,);
162     $template->param( delete_confirmed => 1 );
163 } elsif ( !$bookseller ) {
164     $template->param( NO_BOOKSELLER => 1 );
165 } elsif ($op eq 'export') {
166     print $query->header(
167         -type       => 'text/csv',
168         -attachment => 'basket' . $basket->{'basketno'} . '.csv',
169     );
170     print GetBasketAsCSV($query->param('basketno'), $query);
171     exit;
172 } elsif ($op eq 'email') {
173     my $redirect_url = '/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'};
174     my $err;
175
176     eval {
177         $err = SendAlerts( 'orderacquisition', $query->param('basketno'), 'ACQORDER' );
178     };
179     if ( $@ ) {
180     $redirect_url .= '&email_error='.$@;
181     } elsif ( ref $err and exists $err->{error} and $err->{error} eq "no_email" ) {
182         $redirect_url .= '&email_error=no_email';
183     } elsif ( ref $err and exists $err->{error} and $err->{error} eq "no_basketno" ) {
184         $redirect_url .= '&email_error=no_basketno';
185     } else {
186         $redirect_url .= '&email_ok=1';
187     }
188
189     print $query->redirect($redirect_url)
190
191 } elsif ($op eq 'close') {
192     my $confirm = $query->param('confirm') || $confirm_pref eq '2';
193     if ($confirm) {
194         my $basketno = $query->param('basketno');
195         my $booksellerid = $query->param('booksellerid');
196         $basketno =~ /^\d+$/ and CloseBasket($basketno);
197         # if requested, create basket group, close it and attach the basket
198         if ($query->param('createbasketgroup')) {
199             my $branchcode;
200             if(C4::Context->userenv and C4::Context->userenv->{'branch'}
201               and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET") {
202                 $branchcode = C4::Context->userenv->{'branch'};
203             }
204             my $basketgroupid = NewBasketgroup( { name => $basket->{basketname},
205                             booksellerid => $booksellerid,
206                             deliveryplace => $branchcode,
207                             billingplace => $branchcode,
208                             closed => 1,
209                             });
210             ModBasket( { basketno => $basketno,
211                          basketgroupid => $basketgroupid } );
212             print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
213         } else {
214             print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
215         }
216         exit;
217     } else {
218     $template->param(
219         confirm_close   => "1",
220         booksellerid    => $booksellerid,
221         basketno        => $basket->{'basketno'},
222         basketname      => $basket->{'basketname'},
223         basketgroupname => $basket->{'basketname'},
224     );
225     }
226 } elsif ($op eq 'reopen') {
227     ReopenBasket($query->param('basketno'));
228     print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
229 }
230 elsif ( $op eq 'ediorder' ) {
231     edi_close_and_order()
232 } elsif ( $op eq 'mod_users' ) {
233     my $basketusers_ids = $query->param('users_ids');
234     my @basketusers = split( /:/, $basketusers_ids );
235     ModBasketUsers($basketno, @basketusers);
236     print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
237     exit;
238 } elsif ( $op eq 'mod_branch' ) {
239     my $branch = $query->param('branch');
240     $branch = undef if(defined $branch and $branch eq '');
241     ModBasket({
242         basketno => $basket->{basketno},
243         branch   => $branch
244     });
245     print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
246     exit;
247 } else {
248     my @branches_loop;
249     # get librarian branch...
250     if ( C4::Context->preference("IndependentBranches") ) {
251         my $userenv = C4::Context->userenv;
252         unless ( C4::Context->IsSuperLibrarian() ) {
253             my $validtest = ( $basket->{creationdate} eq '' )
254               || ( $userenv->{branch} eq $basket->{branch} )
255               || ( $userenv->{branch} eq '' )
256               || ( $basket->{branch}  eq '' );
257             unless ($validtest) {
258                 print $query->redirect("../mainpage.pl");
259                 exit 0;
260             }
261         }
262
263         if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
264             push @branches_loop, {
265                 branchcode => $userenv->{branch},
266                 branchname => $userenv->{branchname},
267                 selected => 1,
268             };
269         }
270     } else {
271         # get branches
272         my $branches = Koha::Libraries->search( {}, { order_by => ['branchname'] } )->unblessed;
273         foreach my $branch (@$branches) {
274             my $selected = 0;
275             if (defined $basket->{branch}) {
276                 $selected = 1 if $branch->{branchcode} eq $basket->{branch};
277             } else {
278                 $selected = 1 if $branch->{branchcode} eq C4::Context->userenv->{branch};
279             }
280             push @branches_loop, {
281                 branchcode => $branch->{branchcode},
282                 branchname => $branch->{branchname},
283                 selected => $selected
284             };
285         }
286     }
287
288 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
289     my ($basketgroup, $basketgroups);
290     my $staffuser = GetMember(borrowernumber => $loggedinuser);
291     if ($basket->{closedate} && haspermission($staffuser->{userid}, { acquisition => 'group_manage'} )) {
292         $basketgroups = GetBasketgroups($basket->{booksellerid});
293         for my $bg ( @{$basketgroups} ) {
294             if ($basket->{basketgroupid} && $basket->{basketgroupid} == $bg->{id}){
295                 $bg->{default} = 1;
296                 $basketgroup = $bg;
297             }
298         }
299     }
300
301     # if the basket is closed, calculate estimated delivery date
302     my $estimateddeliverydate;
303     if( $basket->{closedate} ) {
304         my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
305         ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->{deliverytime});
306         $estimateddeliverydate = sprintf( "%04d-%02d-%02d", $year, $month, $day );
307     }
308
309     # if new basket, pre-fill infos
310     $basket->{creationdate} = ""            unless ( $basket->{creationdate} );
311     $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
312     $debug
313       and warn sprintf
314       "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
315       $basket->{creationdate}, $basket->{authorisedby};
316
317     my @basketusers_ids = GetBasketUsers($basketno);
318     my @basketusers;
319     foreach my $basketuser_id (@basketusers_ids) {
320         my $basketuser = GetMember(borrowernumber => $basketuser_id);
321         push @basketusers, $basketuser if $basketuser;
322     }
323
324     my $active_currency = Koha::Acquisition::Currencies->get_active;
325
326     my @orders = GetOrders( $basketno );
327     my @books_loop;
328
329     my @book_foot_loop;
330     my %foot;
331     my $total_quantity = 0;
332     my $total_gste = 0;
333     my $total_gsti = 0;
334     my $total_gstvalue = 0;
335     for my $order (@orders) {
336         $order = C4::Acquisition::populate_order_with_prices({ order => $order, booksellerid => $booksellerid, ordering => 1 });
337         my $line = get_order_infos( $order, $bookseller);
338         if ( $line->{uncertainprice} ) {
339             $template->param( uncertainprices => 1 );
340         }
341
342         push @books_loop, $line;
343
344         $foot{$$line{gstrate}}{gstrate} = $$line{gstrate};
345         $foot{$$line{gstrate}}{gstvalue} += $$line{gstvalue};
346         $total_gstvalue += $$line{gstvalue};
347         $foot{$$line{gstrate}}{quantity}  += $$line{quantity};
348         $total_quantity += $$line{quantity};
349         $foot{$$line{gstrate}}{totalgste} += $$line{totalgste};
350         $total_gste += $$line{totalgste};
351         $foot{$$line{gstrate}}{totalgsti} += $$line{totalgsti};
352         $total_gsti += $$line{totalgsti};
353     }
354
355     push @book_foot_loop, map {$_} values %foot;
356
357     # Get cancelled orders
358     my @cancelledorders = GetOrders($basketno, { cancelled => 1 });
359     my @cancelledorders_loop;
360     for my $order (@cancelledorders) {
361         $order = C4::Acquisition::populate_order_with_prices({ order => $order, booksellerid => $booksellerid, ordering => 1 });
362         my $line = get_order_infos( $order, $bookseller);
363         push @cancelledorders_loop, $line;
364     }
365
366     my $contract = GetContract({
367         contractnumber => $basket->{contractnumber}
368     });
369
370     if ($basket->{basketgroupid}){
371         $basketgroup = GetBasketgroup($basket->{basketgroupid});
372     }
373     my $borrower= GetMember('borrowernumber' => $loggedinuser);
374     my $budgets = GetBudgetHierarchy;
375     my $has_budgets = 0;
376     foreach my $r (@{$budgets}) {
377         if (!defined $r->{budget_amount} || $r->{budget_amount} == 0) {
378             next;
379         }
380         next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
381
382         $has_budgets = 1;
383         last;
384     }
385
386     $template->param(
387         basketno             => $basketno,
388         basket               => $basket,
389         basketname           => $basket->{'basketname'},
390         basketbranchcode     => $basket->{branch},
391         basketnote           => $basket->{note},
392         basketbooksellernote => $basket->{booksellernote},
393         basketcontractno     => $basket->{contractnumber},
394         basketcontractname   => $contract->{contractname},
395         branches_loop        => \@branches_loop,
396         creationdate         => $basket->{creationdate},
397         authorisedby         => $basket->{authorisedby},
398         authorisedbyname     => $basket->{authorisedbyname},
399         users_ids            => join(':', @basketusers_ids),
400         users                => \@basketusers,
401         closedate            => $basket->{closedate},
402         estimateddeliverydate=> $estimateddeliverydate,
403         is_standing          => $basket->{is_standing},
404         deliveryplace        => $basket->{deliveryplace},
405         billingplace         => $basket->{billingplace},
406         active               => $bookseller->{'active'},
407         booksellerid         => $bookseller->{'id'},
408         name                 => $bookseller->{'name'},
409         books_loop           => \@books_loop,
410         book_foot_loop       => \@book_foot_loop,
411         cancelledorders_loop => \@cancelledorders_loop,
412         total_quantity       => $total_quantity,
413         total_gste           => sprintf( "%.2f", $total_gste ),
414         total_gsti           => sprintf( "%.2f", $total_gsti ),
415         total_gstvalue       => sprintf( "%.2f", $total_gstvalue ),
416         currency             => $active_currency->currency,
417         listincgst           => $bookseller->{listincgst},
418         basketgroups         => $basketgroups,
419         basketgroup          => $basketgroup,
420         grouped              => $basket->{basketgroupid},
421         # The double negatives and booleans here mean:
422         # "A basket cannot be closed if there are no orders in it or it's a standing order basket."
423         #
424         # (The template has another implicit restriction that the order cannot be closed if there
425         # are any orders with uncertain prices.)
426         unclosable           => @orders ? $basket->{is_standing} : 1,
427         has_budgets          => $has_budgets,
428         duplinbatch          => $duplinbatch,
429     );
430 }
431
432 sub get_order_infos {
433     my $order = shift;
434     my $bookseller = shift;
435     my $qty = $order->{'quantity'} || 0;
436     if ( !defined $order->{quantityreceived} ) {
437         $order->{quantityreceived} = 0;
438     }
439     my $budget = GetBudget($order->{budget_id});
440     my $basket = GetBasket($order->{basketno});
441
442     my %line = %{ $order };
443     # Don't show unreceived standing orders as received
444     $line{order_received} = ( $qty == $order->{'quantityreceived'} && ( $basket->{is_standing} ? $qty : 1 ) );
445     $line{basketno}       = $basketno;
446     $line{budget_name}    = $budget->{budget_name};
447
448     if ( $line{uncertainprice} ) {
449         $line{rrpgste} .= ' (Uncertain)';
450     }
451     if ( $line{'title'} ) {
452         my $volume      = $order->{'volume'};
453         my $seriestitle = $order->{'seriestitle'};
454         $line{'title'} .= " / $seriestitle" if $seriestitle;
455         $line{'title'} .= " / $volume"      if $volume;
456     }
457
458     my $biblionumber = $order->{'biblionumber'};
459     my $countbiblio = CountBiblioInOrders($biblionumber);
460     my $ordernumber = $order->{'ordernumber'};
461     my @subscriptions = GetSubscriptionsId ($biblionumber);
462     my $itemcount = GetItemsCount($biblionumber);
463     my $holds  = GetHolds ($biblionumber);
464     my @items = GetItemnumbersFromOrder( $ordernumber );
465     my $itemholds;
466     foreach my $item (@items){
467         my $nb = GetItemHolds($biblionumber, $item);
468         if ($nb){
469             $itemholds += $nb;
470         }
471     }
472     # 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
473     $line{can_del_bib}          = 1 if $countbiblio <= 1 && $itemcount == scalar @items && !(@subscriptions) && !($holds);
474     $line{items}                = ($itemcount) - (scalar @items);
475     $line{left_item}            = 1 if $line{items} >= 1;
476     $line{left_biblio}          = 1 if $countbiblio > 1;
477     $line{biblios}              = $countbiblio - 1;
478     $line{left_subscription}    = 1 if scalar @subscriptions >= 1;
479     $line{subscriptions}        = scalar @subscriptions;
480     ($holds >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
481     $line{left_holds_on_order}  = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
482     $line{holds}                = $holds;
483     $line{holds_on_order}       = $itemholds?$itemholds:$holds if $line{left_holds_on_order};
484
485
486     my $suggestion   = GetSuggestionInfoFromBiblionumber($line{biblionumber});
487     $line{suggestionid}         = $$suggestion{suggestionid};
488     $line{surnamesuggestedby}   = $$suggestion{surnamesuggestedby};
489     $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
490
491     foreach my $key (qw(transferred_from transferred_to)) {
492         if ($line{$key}) {
493             my $order = GetOrder($line{$key});
494             my $bookseller = Koha::Acquisition::Bookseller->fetch({ id => $basket->{booksellerid} });
495             $line{$key} = {
496                 order => $order,
497                 basket => $basket,
498                 bookseller => $bookseller,
499                 timestamp => $line{$key . '_timestamp'},
500             };
501         }
502     }
503
504     return \%line;
505 }
506
507 output_html_with_http_headers $query, $cookie, $template->output;
508
509
510 sub edi_close_and_order {
511     my $confirm = $query->param('confirm') || $confirm_pref eq '2';
512     if ($confirm) {
513             my $edi_params = {
514                 basketno => $basketno,
515                 ean    => $ean,
516             };
517             if ( $basket->{branch} ) {
518                 $edi_params->{branchcode} = $basket->{branch};
519             }
520             if ( create_edi_order($edi_params) ) {
521                 #$template->param( edifile => 1 );
522             }
523         CloseBasket($basketno);
524
525         # if requested, create basket group, close it and attach the basket
526         if ( $query->param('createbasketgroup') ) {
527             my $branchcode;
528             if (    C4::Context->userenv
529                 and C4::Context->userenv->{'branch'}
530                 and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET" )
531             {
532                 $branchcode = C4::Context->userenv->{'branch'};
533             }
534             my $basketgroupid = NewBasketgroup(
535                 {
536                     name          => $basket->{basketname},
537                     booksellerid  => $booksellerid,
538                     deliveryplace => $branchcode,
539                     billingplace  => $branchcode,
540                     closed        => 1,
541                 }
542             );
543             ModBasket(
544                 {
545                     basketno      => $basketno,
546                     basketgroupid => $basketgroupid
547                 }
548             );
549             print $query->redirect(
550 "/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=$booksellerid&closed=1"
551             );
552         }
553         else {
554             print $query->redirect(
555                 "/cgi-bin/koha/acqui/booksellers.pl?booksellerid=$booksellerid"
556             );
557         }
558         exit;
559     }
560     else {
561         $template->param(
562             edi_confirm     => 1,
563             booksellerid    => $booksellerid,
564             basketno        => $basket->{basketno},
565             basketname      => $basket->{basketname},
566             basketgroupname => $basket->{basketname},
567         );
568         if ($ean) {
569             $template->param( ean => $ean );
570         }
571
572     }
573     return;
574 }