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