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>.
36 use Koha
::Acquisition
::Baskets
;
37 use Koha
::Acquisition
::Booksellers
;
38 use Koha
::Acquisition
::Orders
;
40 use C4
::Letters qw
/SendAlerts/;
41 use Date
::Calc qw
/Add_Delta_Days/;
43 use Koha
::EDI
qw( create_edi_order get_edifact_ean );
44 use Koha
::CsvProfiles
;
47 use Koha
::AdditionalFields
;
55 This script display all informations about basket for the supplier given
56 on input arg. Moreover, it allows us to add a new order for this supplier from
57 an existing record, a suggestion or a new record.
69 the supplier this script have to display the basket.
77 our $query = CGI
->new;
78 our $basketno = $query->param('basketno');
79 our $ean = $query->param('ean');
80 our $booksellerid = $query->param('booksellerid');
81 my $duplinbatch = $query->param('duplinbatch');
83 our ( $template, $loggedinuser, $cookie, $userflags ) = get_template_and_user
(
85 template_name
=> "acqui/basket.tt",
88 flagsrequired
=> { acquisition
=> 'order_manage' },
93 my $logged_in_patron = Koha
::Patrons
->find( $loggedinuser );
95 our $basket = GetBasket
($basketno);
96 $booksellerid = $basket->{booksellerid
} unless $booksellerid;
97 my $bookseller = Koha
::Acquisition
::Booksellers
->find( $booksellerid );
98 my $schema = Koha
::Database
->new()->schema();
99 my $rs = $schema->resultset('VendorEdiAccount')->search(
100 { vendor_id
=> $booksellerid, } );
101 $template->param( ediaccount
=> ($rs->count > 0));
103 unless (CanUserManageBasket
($loggedinuser, $basket, $userflags)) {
105 cannot_manage_basket
=> 1,
106 basketno
=> $basketno,
107 basketname
=> $basket->{basketname
},
108 booksellerid
=> $booksellerid,
109 booksellername
=> $bookseller->name,
111 output_html_with_http_headers
$query, $cookie, $template->output;
115 # FIXME : what about the "discount" percentage?
116 # FIXME : the query->param('booksellerid') below is probably useless. The bookseller is always known from the basket
117 # if no booksellerid in parameter, get it from basket
118 # warn "=>".$basket->{booksellerid};
119 my $op = $query->param('op') // 'list';
121 our $confirm_pref= C4
::Context
->preference("BasketConfirmations") || '1';
122 $template->param( skip_confirm_reopen
=> 1) if $confirm_pref eq '2';
126 if ( $op eq 'delete_confirm' ) {
128 output_and_exit
( $query, $cookie, $template, 'insufficient_permission' )
129 unless $logged_in_patron->has_permission( { acquisition
=> 'delete_baskets' } );
131 my $basketno = $query->param('basketno');
132 my $delbiblio = $query->param('delbiblio');
133 my $basket_obj = Koha
::Acquisition
::Baskets
->find($basketno);
135 my $orders = $basket_obj->orders;
137 my @cannotdelbiblios;
139 while ( my $order = $orders->next ) {
141 $order->cancel({ delete_biblio
=> $delbiblio });
142 my @messages = @
{ $order->messages };
144 if ( scalar @messages > 0 ) {
146 my $biblio = $order->biblio;
148 push @cannotdelbiblios, {
149 biblionumber
=> $biblio->id,
150 title
=> $biblio->title // '',
151 author
=> $biblio->author // '',
152 countbiblio
=> $biblio->active_orders->count,
153 itemcount
=> $biblio->items->count,
154 subscriptions
=> $biblio->subscriptions->count,
159 $template->param( cannotdelbiblios
=> \
@cannotdelbiblios );
164 delete_confirmed
=> 1,
165 booksellername
=> $bookseller->name,
166 booksellerid
=> $booksellerid,
168 } elsif ( !$bookseller ) {
169 $template->param( NO_BOOKSELLER
=> 1 );
170 } elsif ($op eq 'export') {
171 print $query->header(
173 -attachment
=> 'basket' . $basket->{'basketno'} . '.csv',
175 my $csv_profile_id = $query->param('csv_profile');
176 print GetBasketAsCSV
( scalar $query->param('basketno'), $query, $csv_profile_id ); # if no csv_profile_id passed, using default rows
178 } elsif ($op eq 'email') {
180 SendAlerts
( 'orderacquisition', $query->param('basketno'), 'ACQORDER' );
183 push @messages, { type
=> 'error', code
=> $@
};
184 } elsif ( ref $err and exists $err->{error
} ) {
185 push @messages, { type
=> 'error', code
=> $err->{error
} };
187 push @messages, { type
=> 'message', code
=> 'email_sent' };
191 } elsif ($op eq 'close') {
192 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
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')) {
200 if(C4
::Context
->userenv and C4
::Context
->userenv->{'branch'}) {
201 $branchcode = C4
::Context
->userenv->{'branch'};
203 my $basketgroupid = NewBasketgroup
( { name
=> $basket->{basketname
},
204 booksellerid
=> $booksellerid,
205 deliveryplace
=> $branchcode,
206 billingplace
=> $branchcode,
209 ModBasket
( { basketno
=> $basketno,
210 basketgroupid
=> $basketgroupid } );
211 print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
213 print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
218 confirm_close
=> "1",
219 booksellerid
=> $booksellerid,
220 booksellername
=> $bookseller->name,
221 basketno
=> $basket->{'basketno'},
222 basketname
=> $basket->{'basketname'},
223 basketgroupname
=> $basket->{'basketname'},
226 } elsif ($op eq 'reopen') {
227 ReopenBasket
(scalar $query->param('basketno'));
228 print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
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");
238 } elsif ( $op eq 'mod_branch' ) {
239 my $branch = $query->param('branch');
240 $branch = undef if(defined $branch and $branch eq '');
242 basketno
=> $basket->{basketno
},
245 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
249 if ( $op eq 'list' ) {
251 # get librarian branch...
252 if ( C4
::Context
->preference("IndependentBranches") ) {
253 my $userenv = C4
::Context
->userenv;
254 unless ( C4
::Context
->IsSuperLibrarian() ) {
255 my $validtest = ( $basket->{creationdate
} eq '' )
256 || ( $userenv->{branch
} eq $basket->{branch
} )
257 || ( $userenv->{branch
} eq '' )
258 || ( $basket->{branch
} eq '' );
259 unless ($validtest) {
260 print $query->redirect("../mainpage.pl");
265 if (!defined $basket->{branch
} or $basket->{branch
} eq $userenv->{branch
}) {
266 push @branches_loop, {
267 branchcode
=> $userenv->{branch
},
268 branchname
=> $userenv->{branchname
},
274 my $branches = Koha
::Libraries
->search( {}, { order_by
=> ['branchname'] } )->unblessed;
275 foreach my $branch (@
$branches) {
277 if (defined $basket->{branch
}) {
278 $selected = 1 if $branch->{branchcode
} eq $basket->{branch
};
280 $selected = 1 if $branch->{branchcode
} eq C4
::Context
->userenv->{branch
};
282 push @branches_loop, {
283 branchcode
=> $branch->{branchcode
},
284 branchname
=> $branch->{branchname
},
285 selected
=> $selected
290 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
291 my ($basketgroup, $basketgroups);
292 my $patron = Koha
::Patrons
->find($loggedinuser);
293 if ($basket->{closedate
} && haspermission
($patron->userid, { acquisition
=> 'group_manage'} )) {
294 $basketgroups = GetBasketgroups
($basket->{booksellerid
});
295 for my $bg ( @
{$basketgroups} ) {
296 if ($basket->{basketgroupid
} && $basket->{basketgroupid
} == $bg->{id
}){
303 # if the basket is closed, calculate estimated delivery date
304 my $estimateddeliverydate;
305 if( $basket->{closedate
} ) {
306 my ($year, $month, $day) = ($basket->{closedate
} =~ /(\d+)-(\d+)-(\d+)/);
307 ($year, $month, $day) = Add_Delta_Days
($year, $month, $day, $bookseller->deliverytime);
308 $estimateddeliverydate = sprintf( "%04d-%02d-%02d", $year, $month, $day );
311 # if new basket, pre-fill infos
312 $basket->{creationdate
} = "" unless ( $basket->{creationdate
} );
313 $basket->{authorisedby
} = $loggedinuser unless ( $basket->{authorisedby
} );
316 "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
317 $basket->{creationdate
}, $basket->{authorisedby
};
319 my @basketusers_ids = GetBasketUsers
($basketno);
321 foreach my $basketuser_id (@basketusers_ids) {
322 # FIXME Could be improved with a search -in
323 my $basket_patron = Koha
::Patrons
->find( $basketuser_id );
324 push @basketusers, $basket_patron if $basket_patron;
327 my $active_currency = Koha
::Acquisition
::Currencies
->get_active;
329 my @orders = GetOrders
( $basketno );
334 my $total_quantity = 0;
335 my $total_tax_excluded = 0;
336 my $total_tax_included = 0;
337 my $total_tax_value = 0;
338 for my $order (@orders) {
339 my $line = get_order_infos
( $order, $bookseller);
340 if ( $line->{uncertainprice
} ) {
341 $template->param( uncertainprices
=> 1 );
344 $line->{tax_rate
} = $line->{tax_rate_on_ordering
} // 0;
345 $line->{tax_value
} = $line->{tax_value_on_ordering
} // 0;
347 push @books_loop, $line;
349 $foot{$$line{tax_rate
}}{tax_rate
} = $$line{tax_rate
};
350 $foot{$$line{tax_rate
}}{tax_value
} += get_rounded_price
($$line{tax_value
});
351 $total_tax_value += $$line{tax_value
};
352 $foot{$$line{tax_rate
}}{quantity
} += get_rounded_price
($$line{quantity
});
353 $total_quantity += $$line{quantity
};
354 $foot{$$line{tax_rate
}}{total_tax_excluded
} += $$line{total_tax_excluded
};
355 $total_tax_excluded += $$line{total_tax_excluded
};
356 $foot{$$line{tax_rate
}}{total_tax_included
} += $$line{total_tax_included
};
357 $total_tax_included += $$line{total_tax_included
};
360 push @book_foot_loop, map {$_} values %foot;
362 # Get cancelled orders
363 my @cancelledorders = GetOrders
($basketno, { cancelled
=> 1 });
364 my @cancelledorders_loop;
365 for my $order (@cancelledorders) {
366 my $line = get_order_infos
( $order, $bookseller);
367 push @cancelledorders_loop, $line;
370 my $contract = GetContract
({
371 contractnumber
=> $basket->{contractnumber
}
374 if ($basket->{basketgroupid
}){
375 $basketgroup = GetBasketgroup
($basket->{basketgroupid
});
377 my $budgets = GetBudgetHierarchy
;
379 foreach my $r (@
{$budgets}) {
380 next unless (CanUserUseBudget
($loggedinuser, $r, $userflags));
387 basketno
=> $basketno,
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 booksellername
=> $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_tax_excluded
=> $total_tax_excluded,
414 total_tax_included
=> $total_tax_included,
415 total_tax_value
=> $total_tax_value,
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."
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 || @cancelledorders ?
$basket->{is_standing
} : 1,
427 has_budgets
=> $has_budgets,
428 duplinbatch
=> $duplinbatch,
429 csv_profiles
=> [ Koha
::CsvProfiles
->search({ type
=> 'sql', used_for
=> 'export_basket' }) ],
430 available_additional_fields
=> [ Koha
::AdditionalFields
->search( { tablename
=> 'aqbasket' } ) ],
431 additional_field_values
=> { map {
432 $_->field->name => $_->value
433 } Koha
::Acquisition
::Baskets
->find($basketno)->additional_field_values->as_list },
437 $template->param( messages
=> \
@messages );
438 output_html_with_http_headers
$query, $cookie, $template->output;
440 sub get_order_infos
{
442 my $bookseller = shift;
443 my $qty = $order->{'quantity'} || 0;
444 if ( !defined $order->{quantityreceived
} ) {
445 $order->{quantityreceived
} = 0;
447 my $budget = GetBudget
($order->{budget_id
});
448 my $basket = GetBasket
($order->{basketno
});
450 my %line = %{ $order };
451 # Don't show unreceived standing orders as received
452 $line{order_received
} = ( $qty == $order->{'quantityreceived'} && ( $basket->{is_standing
} ?
$qty : 1 ) );
453 $line{basketno
} = $basketno;
454 $line{budget_name
} = $budget->{budget_name
};
456 # If we have an actual cost that should be the total, otherwise use the ecost
457 $line{unitprice_tax_included
} += 0;
458 $line{unitprice_tax_excluded
} += 0;
459 my $cost_tax_included = $line{unitprice_tax_included
} || $line{ecost_tax_included
};
460 my $cost_tax_excluded = $line{unitprice_tax_excluded
} || $line{ecost_tax_excluded
};
461 $line{total_tax_included
} = get_rounded_price
($cost_tax_included) * $line{quantity
};
462 $line{total_tax_excluded
} = get_rounded_price
($cost_tax_excluded) * $line{quantity
};
463 $line{tax_value
} = $line{tax_value_on_ordering
};
464 $line{tax_rate
} = $line{tax_rate_on_ordering
};
466 if ( $line{'title'} ) {
467 my $volume = $order->{'volume'};
468 my $seriestitle = $order->{'seriestitle'};
469 $line{'title'} .= " / $seriestitle" if $seriestitle;
470 $line{'title'} .= " / $volume" if $volume;
473 my $biblionumber = $order->{'biblionumber'};
474 if ( $biblionumber ) { # The biblio still exists
475 my $biblio = Koha
::Biblios
->find( $biblionumber );
476 my $countbiblio = $biblio->active_orders->count;
478 my $ordernumber = $order->{'ordernumber'};
479 my $cnt_subscriptions = $biblio->subscriptions->count;
480 my $itemcount = $biblio->items->count;
481 my $holds_count = $biblio->holds->count;
482 my $order = Koha
::Acquisition
::Orders
->find($ordernumber); # FIXME We should certainly do that at the beginning of this sub
483 my $items = $order->items;
484 my $itemholds = $biblio->holds->search({ itemnumber
=> { -in => [ $items->get_column('itemnumber') ] } })->count;
486 # 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
487 $line{can_del_bib
} = 1 if $countbiblio <= 1 && $itemcount == $items->count && !($cnt_subscriptions) && !($holds_count);
488 $line{items
} = $itemcount - $items->count;
489 $line{left_item
} = 1 if $line{items
} >= 1;
490 $line{left_biblio
} = 1 if $countbiblio > 1;
491 $line{biblios
} = $countbiblio - 1;
492 $line{left_subscription
} = 1 if $cnt_subscriptions;
493 $line{subscriptions
} = $cnt_subscriptions;
494 ($holds_count >= 1) ?
$line{left_holds
} = 1 : $line{left_holds
} = 0;
495 $line{left_holds_on_order
} = 1 if $line{left_holds
}==1 && ($line{items
} == 0 || $itemholds );
496 $line{holds
} = $holds_count;
497 $line{holds_on_order
} = $itemholds?
$itemholds:$holds_count if $line{left_holds_on_order
};
498 $line{order_object
} = $order;
502 my $suggestion = GetSuggestionInfoFromBiblionumber
($line{biblionumber
});
503 $line{suggestionid
} = $$suggestion{suggestionid
};
504 $line{surnamesuggestedby
} = $$suggestion{surnamesuggestedby
};
505 $line{firstnamesuggestedby
} = $$suggestion{firstnamesuggestedby
};
507 foreach my $key (qw(transferred_from transferred_to)) {
509 my $order = GetOrder
($line{$key});
510 my $basket = GetBasket
($order->{basketno
});
511 my $bookseller = Koha
::Acquisition
::Booksellers
->find( $basket->{booksellerid
} );
515 bookseller
=> $bookseller,
516 timestamp
=> $line{$key . '_timestamp'},
524 sub edi_close_and_order
{
525 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
528 basketno
=> $basketno,
531 if ( $basket->{branch
} ) {
532 $edi_params->{branchcode
} = $basket->{branch
};
534 if ( create_edi_order
($edi_params) ) {
535 #$template->param( edifile => 1 );
537 CloseBasket
($basketno);
539 # if requested, create basket group, close it and attach the basket
540 if ( $query->param('createbasketgroup') ) {
542 if ( C4
::Context
->userenv
543 and C4
::Context
->userenv->{'branch'} )
545 $branchcode = C4
::Context
->userenv->{'branch'};
547 my $basketgroupid = NewBasketgroup
(
549 name
=> $basket->{basketname
},
550 booksellerid
=> $booksellerid,
551 deliveryplace
=> $branchcode,
552 billingplace
=> $branchcode,
558 basketno
=> $basketno,
559 basketgroupid
=> $basketgroupid
562 print $query->redirect(
563 "/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=$booksellerid&closed=1"
567 print $query->redirect(
568 "/cgi-bin/koha/acqui/booksellers.pl?booksellerid=$booksellerid"
576 booksellerid
=> $booksellerid,
577 basketno
=> $basket->{basketno
},
578 basketname
=> $basket->{basketname
},
579 basketgroupname
=> $basket->{basketname
},
582 $template->param( ean
=> $ean );