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::Booksellers;
38 use C4::Letters qw/SendAlerts/;
39 use Date::Calc qw/Add_Delta_Days/;
41 use Koha::EDI qw( create_edi_order get_edifact_ean );
42 use Koha::CsvProfiles;
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.
65 the supplier this script have to display the basket.
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",
85 flagsrequired => { acquisition => 'order_manage' },
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)) {
100 cannot_manage_basket => 1,
101 basketno => $basketno,
102 basketname => $basket->{basketname},
103 booksellerid => $booksellerid,
104 booksellername => $bookseller->name,
106 output_html_with_http_headers $query, $cookie, $template->output;
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';
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;
140 if ($countbiblio == 0 && $itemcount == 0 && not $cnt_subscriptions ) {
141 $error = DelBiblio($myorder->{biblionumber}) }
143 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
144 title=> $myorder->{'title'},
145 author=> $myorder->{'author'},
146 countbiblio=> $countbiblio,
147 itemcount=>$itemcount,
148 subscriptions => $cnt_subscriptions};
151 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
152 title=> $myorder->{'title'},
153 author=> $myorder->{'author'},
154 othererror=> $error};
157 $template->param( cannotdelbiblios => \@cannotdelbiblios );
160 DelBasket($basketno,);
162 delete_confirmed => 1,
163 booksellername => $bookseller->name,
164 booksellerid => $booksellerid,
166 } elsif ( !$bookseller ) {
167 $template->param( NO_BOOKSELLER => 1 );
168 } elsif ($op eq 'export') {
169 print $query->header(
171 -attachment => 'basket' . $basket->{'basketno'} . '.csv',
173 if ( $query->param('csv_profile') eq 'default'){
174 print GetBasketAsCSV($query->param('basketno'), $query);
176 my $csv_profile_id = $query->param('csv_profile');
177 print GetBasketAsCSV($query->param('basketno'), $query, $csv_profile_id);
180 } elsif ($op eq 'email') {
182 SendAlerts( 'orderacquisition', $query->param('basketno'), 'ACQORDER' );
185 push @messages, { type => 'error', code => $@ };
186 } elsif ( ref $err and exists $err->{error} ) {
187 push @messages, { type => 'error', code => $err->{error} };
189 push @messages, { type => 'message', code => 'email_sent' };
193 } elsif ($op eq 'close') {
194 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
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')) {
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,
212 ModBasket( { basketno => $basketno,
213 basketgroupid => $basketgroupid } );
214 print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
216 print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
221 confirm_close => "1",
222 booksellerid => $booksellerid,
223 booksellername => $bookseller->name,
224 basketno => $basket->{'basketno'},
225 basketname => $basket->{'basketname'},
226 basketgroupname => $basket->{'basketname'},
229 } elsif ($op eq 'reopen') {
230 ReopenBasket(scalar $query->param('basketno'));
231 print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
233 elsif ( $op eq 'ediorder' ) {
234 edi_close_and_order()
235 } elsif ( $op eq 'mod_users' ) {
236 my $basketusers_ids = $query->param('users_ids');
237 my @basketusers = split( /:/, $basketusers_ids );
238 ModBasketUsers($basketno, @basketusers);
239 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
241 } elsif ( $op eq 'mod_branch' ) {
242 my $branch = $query->param('branch');
243 $branch = undef if(defined $branch and $branch eq '');
245 basketno => $basket->{basketno},
248 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
252 if ( $op eq 'list' ) {
254 # get librarian branch...
255 if ( C4::Context->preference("IndependentBranches") ) {
256 my $userenv = C4::Context->userenv;
257 unless ( C4::Context->IsSuperLibrarian() ) {
258 my $validtest = ( $basket->{creationdate} eq '' )
259 || ( $userenv->{branch} eq $basket->{branch} )
260 || ( $userenv->{branch} eq '' )
261 || ( $basket->{branch} eq '' );
262 unless ($validtest) {
263 print $query->redirect("../mainpage.pl");
268 if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
269 push @branches_loop, {
270 branchcode => $userenv->{branch},
271 branchname => $userenv->{branchname},
277 my $branches = Koha::Libraries->search( {}, { order_by => ['branchname'] } )->unblessed;
278 foreach my $branch (@$branches) {
280 if (defined $basket->{branch}) {
281 $selected = 1 if $branch->{branchcode} eq $basket->{branch};
283 $selected = 1 if $branch->{branchcode} eq C4::Context->userenv->{branch};
285 push @branches_loop, {
286 branchcode => $branch->{branchcode},
287 branchname => $branch->{branchname},
288 selected => $selected
293 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
294 my ($basketgroup, $basketgroups);
295 my $patron = Koha::Patrons->find($loggedinuser);
296 if ($basket->{closedate} && haspermission($patron->userid, { acquisition => 'group_manage'} )) {
297 $basketgroups = GetBasketgroups($basket->{booksellerid});
298 for my $bg ( @{$basketgroups} ) {
299 if ($basket->{basketgroupid} && $basket->{basketgroupid} == $bg->{id}){
306 # if the basket is closed, calculate estimated delivery date
307 my $estimateddeliverydate;
308 if( $basket->{closedate} ) {
309 my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
310 ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->deliverytime);
311 $estimateddeliverydate = sprintf( "%04d-%02d-%02d", $year, $month, $day );
314 # if new basket, pre-fill infos
315 $basket->{creationdate} = "" unless ( $basket->{creationdate} );
316 $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
319 "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
320 $basket->{creationdate}, $basket->{authorisedby};
322 my @basketusers_ids = GetBasketUsers($basketno);
324 foreach my $basketuser_id (@basketusers_ids) {
325 # FIXME Could be improved with a search -in
326 my $basket_patron = Koha::Patrons->find( $basketuser_id );
327 push @basketusers, $basket_patron if $basket_patron;
330 my $active_currency = Koha::Acquisition::Currencies->get_active;
332 my @orders = GetOrders( $basketno );
337 my $total_quantity = 0;
338 my $total_tax_excluded = 0;
339 my $total_tax_included = 0;
340 my $total_tax_value = 0;
341 for my $order (@orders) {
342 my $line = get_order_infos( $order, $bookseller);
343 if ( $line->{uncertainprice} ) {
344 $template->param( uncertainprices => 1 );
347 $line->{tax_rate} = $line->{tax_rate_on_ordering};
348 $line->{tax_value} = $line->{tax_value_on_ordering};
350 push @books_loop, $line;
352 $foot{$$line{tax_rate}}{tax_rate} = $$line{tax_rate};
353 $foot{$$line{tax_rate}}{tax_value} += $$line{tax_value};
354 $total_tax_value += $$line{tax_value};
355 $foot{$$line{tax_rate}}{quantity} += $$line{quantity};
356 $total_quantity += $$line{quantity};
357 $foot{$$line{tax_rate}}{total_tax_excluded} += $$line{total_tax_excluded};
358 $total_tax_excluded += $$line{total_tax_excluded};
359 $foot{$$line{tax_rate}}{total_tax_included} += $$line{total_tax_included};
360 $total_tax_included += $$line{total_tax_included};
363 push @book_foot_loop, map {$_} values %foot;
365 # Get cancelled orders
366 my @cancelledorders = GetOrders($basketno, { cancelled => 1 });
367 my @cancelledorders_loop;
368 for my $order (@cancelledorders) {
369 my $line = get_order_infos( $order, $bookseller);
370 push @cancelledorders_loop, $line;
373 my $contract = GetContract({
374 contractnumber => $basket->{contractnumber}
377 if ($basket->{basketgroupid}){
378 $basketgroup = GetBasketgroup($basket->{basketgroupid});
380 my $budgets = GetBudgetHierarchy;
382 foreach my $r (@{$budgets}) {
383 if (!defined $r->{budget_amount} || $r->{budget_amount} == 0) {
386 next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
394 basketno => $basketno,
396 basketname => $basket->{'basketname'},
397 basketbranchcode => $basket->{branch},
398 basketnote => $basket->{note},
399 basketbooksellernote => $basket->{booksellernote},
400 basketcontractno => $basket->{contractnumber},
401 basketcontractname => $contract->{contractname},
402 branches_loop => \@branches_loop,
403 creationdate => $basket->{creationdate},
404 authorisedby => $basket->{authorisedby},
405 authorisedbyname => $basket->{authorisedbyname},
406 users_ids => join(':', @basketusers_ids),
407 users => \@basketusers,
408 closedate => $basket->{closedate},
409 estimateddeliverydate=> $estimateddeliverydate,
410 is_standing => $basket->{is_standing},
411 deliveryplace => $basket->{deliveryplace},
412 billingplace => $basket->{billingplace},
413 active => $bookseller->active,
414 booksellerid => $bookseller->id,
415 booksellername => $bookseller->name,
416 books_loop => \@books_loop,
417 book_foot_loop => \@book_foot_loop,
418 cancelledorders_loop => \@cancelledorders_loop,
419 total_quantity => $total_quantity,
420 total_tax_excluded => $total_tax_excluded,
421 total_tax_included => $total_tax_included,
422 total_tax_value => $total_tax_value,
423 currency => $active_currency->currency,
424 listincgst => $bookseller->listincgst,
425 basketgroups => $basketgroups,
426 basketgroup => $basketgroup,
427 grouped => $basket->{basketgroupid},
428 # The double negatives and booleans here mean:
429 # "A basket cannot be closed if there are no orders in it or it's a standing order basket."
431 # (The template has another implicit restriction that the order cannot be closed if there
432 # are any orders with uncertain prices.)
433 unclosable => @orders ? $basket->{is_standing} : 1,
434 has_budgets => $has_budgets,
435 duplinbatch => $duplinbatch,
436 csv_profiles => [ Koha::CsvProfiles->search({ type => 'sql', used_for => 'export_basket' }) ],
440 $template->param( messages => \@messages );
441 output_html_with_http_headers $query, $cookie, $template->output;
443 sub get_order_infos {
445 my $bookseller = shift;
446 my $qty = $order->{'quantity'} || 0;
447 if ( !defined $order->{quantityreceived} ) {
448 $order->{quantityreceived} = 0;
450 my $budget = GetBudget($order->{budget_id});
451 my $basket = GetBasket($order->{basketno});
453 my %line = %{ $order };
454 # Don't show unreceived standing orders as received
455 $line{order_received} = ( $qty == $order->{'quantityreceived'} && ( $basket->{is_standing} ? $qty : 1 ) );
456 $line{basketno} = $basketno;
457 $line{budget_name} = $budget->{budget_name};
459 $line{total_tax_included} = $line{ecost_tax_included} * $line{quantity};
460 $line{total_tax_excluded} = $line{ecost_tax_excluded} * $line{quantity};
461 $line{tax_value} = $line{tax_value_on_ordering};
462 $line{tax_rate} = $line{tax_rate_on_ordering};
464 if ( $line{uncertainprice} ) {
465 $line{rrp_tax_excluded} .= ' (Uncertain)';
467 if ( $line{'title'} ) {
468 my $volume = $order->{'volume'};
469 my $seriestitle = $order->{'seriestitle'};
470 $line{'title'} .= " / $seriestitle" if $seriestitle;
471 $line{'title'} .= " / $volume" if $volume;
474 my $biblionumber = $order->{'biblionumber'};
475 if ( $biblionumber ) { # The biblio still exists
476 my $biblio = Koha::Biblios->find( $biblionumber );
477 my $countbiblio = CountBiblioInOrders($biblionumber);
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 @items = GetItemnumbersFromOrder( $ordernumber );
483 my $itemholds = $biblio->holds->search({ itemnumber => { -in => \@items } })->count;
485 # 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
486 $line{can_del_bib} = 1 if $countbiblio <= 1 && $itemcount == scalar @items && !($cnt_subscriptions) && !($holds_count);
487 $line{items} = ($itemcount) - (scalar @items);
488 $line{left_item} = 1 if $line{items} >= 1;
489 $line{left_biblio} = 1 if $countbiblio > 1;
490 $line{biblios} = $countbiblio - 1;
491 $line{left_subscription} = 1 if $cnt_subscriptions;
492 $line{subscriptions} = $cnt_subscriptions;
493 ($holds_count >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
494 $line{left_holds_on_order} = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
495 $line{holds} = $holds_count;
496 $line{holds_on_order} = $itemholds?$itemholds:$holds_count if $line{left_holds_on_order};
500 my $suggestion = GetSuggestionInfoFromBiblionumber($line{biblionumber});
501 $line{suggestionid} = $$suggestion{suggestionid};
502 $line{surnamesuggestedby} = $$suggestion{surnamesuggestedby};
503 $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
505 foreach my $key (qw(transferred_from transferred_to)) {
507 my $order = GetOrder($line{$key});
508 my $basket = GetBasket($order->{basketno});
509 my $bookseller = Koha::Acquisition::Booksellers->find( $basket->{booksellerid} );
513 bookseller => $bookseller,
514 timestamp => $line{$key . '_timestamp'},
522 sub edi_close_and_order {
523 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
526 basketno => $basketno,
529 if ( $basket->{branch} ) {
530 $edi_params->{branchcode} = $basket->{branch};
532 if ( create_edi_order($edi_params) ) {
533 #$template->param( edifile => 1 );
535 CloseBasket($basketno);
537 # if requested, create basket group, close it and attach the basket
538 if ( $query->param('createbasketgroup') ) {
540 if ( C4::Context->userenv
541 and C4::Context->userenv->{'branch'}
542 and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET" )
544 $branchcode = C4::Context->userenv->{'branch'};
546 my $basketgroupid = NewBasketgroup(
548 name => $basket->{basketname},
549 booksellerid => $booksellerid,
550 deliveryplace => $branchcode,
551 billingplace => $branchcode,
557 basketno => $basketno,
558 basketgroupid => $basketgroupid
561 print $query->redirect(
562 "/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=$booksellerid&closed=1"
566 print $query->redirect(
567 "/cgi-bin/koha/acqui/booksellers.pl?booksellerid=$booksellerid"
575 booksellerid => $booksellerid,
576 basketno => $basket->{basketno},
577 basketname => $basket->{basketname},
578 basketgroupname => $basket->{basketname},
581 $template->param( ean => $ean );