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