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.
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",
89 flagsrequired => { acquisition => 'order_manage' },
94 our $basket = GetBasket($basketno);
95 $booksellerid = $basket->{booksellerid} unless $booksellerid;
96 my $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
97 my $schema = Koha::Database->new()->schema();
98 my $rs = $schema->resultset('VendorEdiAccount')->search(
99 { vendor_id => $booksellerid, } );
100 $template->param( ediaccount => ($rs->count > 0));
102 unless (CanUserManageBasket($loggedinuser, $basket, $userflags)) {
104 cannot_manage_basket => 1,
105 basketno => $basketno,
106 basketname => $basket->{basketname},
107 booksellerid => $booksellerid,
108 booksellername => $bookseller->name,
110 output_html_with_http_headers $query, $cookie, $template->output;
114 # FIXME : what about the "discount" percentage?
115 # FIXME : the query->param('booksellerid') below is probably useless. The bookseller is always known from the basket
116 # if no booksellerid in parameter, get it from basket
117 # warn "=>".$basket->{booksellerid};
118 my $op = $query->param('op') // 'list';
120 our $confirm_pref= C4::Context->preference("BasketConfirmations") || '1';
121 $template->param( skip_confirm_reopen => 1) if $confirm_pref eq '2';
125 if ( $op eq 'delete_confirm' ) {
126 my $basketno = $query->param('basketno');
127 my $delbiblio = $query->param('delbiblio');
128 my @orders = GetOrders($basketno);
129 #Delete all orders included in that basket, and all items received.
130 foreach my $myorder (@orders){
131 DelOrder($myorder->{biblionumber},$myorder->{ordernumber});
133 # if $delbiblio = 1, delete the records if possible
134 if ((defined $delbiblio)and ($delbiblio ==1)){
135 my @cannotdelbiblios ;
136 foreach my $myorder (@orders){
137 my $biblionumber = $myorder->{'biblionumber'};
138 my $biblio = Koha::Biblios->find( $biblionumber );
139 my $countbiblio = $biblio->active_orders->count;
140 my $ordernumber = $myorder->{'ordernumber'};
141 my $cnt_subscriptions = $biblio->subscriptions->count;
142 my $itemcount = $biblio->items->count;
144 if ($countbiblio == 0 && $itemcount == 0 && not $cnt_subscriptions ) {
145 $error = DelBiblio($myorder->{biblionumber}) }
147 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
148 title=> $myorder->{'title'},
149 author=> $myorder->{'author'},
150 countbiblio=> $countbiblio,
151 itemcount=>$itemcount,
152 subscriptions => $cnt_subscriptions};
155 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
156 title=> $myorder->{'title'},
157 author=> $myorder->{'author'},
158 othererror=> $error};
161 $template->param( cannotdelbiblios => \@cannotdelbiblios );
164 DelBasket($basketno,);
166 delete_confirmed => 1,
167 booksellername => $bookseller->name,
168 booksellerid => $booksellerid,
170 } elsif ( !$bookseller ) {
171 $template->param( NO_BOOKSELLER => 1 );
172 } elsif ($op eq 'export') {
173 print $query->header(
175 -attachment => 'basket' . $basket->{'basketno'} . '.csv',
177 my $csv_profile_id = $query->param('csv_profile');
178 print GetBasketAsCSV( scalar $query->param('basketno'), $query, $csv_profile_id ); # if no csv_profile_id passed, using default rows
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 $branchcode = C4::Context->userenv->{'branch'};
205 my $basketgroupid = NewBasketgroup( { name => $basket->{basketname},
206 booksellerid => $booksellerid,
207 deliveryplace => $branchcode,
208 billingplace => $branchcode,
211 ModBasket( { basketno => $basketno,
212 basketgroupid => $basketgroupid } );
213 print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
215 print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
220 confirm_close => "1",
221 booksellerid => $booksellerid,
222 booksellername => $bookseller->name,
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");
240 } elsif ( $op eq 'mod_branch' ) {
241 my $branch = $query->param('branch');
242 $branch = undef if(defined $branch and $branch eq '');
244 basketno => $basket->{basketno},
247 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
251 if ( $op eq 'list' ) {
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");
267 if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
268 push @branches_loop, {
269 branchcode => $userenv->{branch},
270 branchname => $userenv->{branchname},
276 my $branches = Koha::Libraries->search( {}, { order_by => ['branchname'] } )->unblessed;
277 foreach my $branch (@$branches) {
279 if (defined $basket->{branch}) {
280 $selected = 1 if $branch->{branchcode} eq $basket->{branch};
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}){
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} );
318 "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
319 $basket->{creationdate}, $basket->{authorisedby};
321 my @basketusers_ids = GetBasketUsers($basketno);
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 );
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} // 0;
347 $line->{tax_value} = $line->{tax_value_on_ordering} // 0;
349 push @books_loop, $line;
351 $foot{$$line{tax_rate}}{tax_rate} = $$line{tax_rate};
352 $foot{$$line{tax_rate}}{tax_value} += get_rounded_price($$line{tax_value});
353 $total_tax_value += $$line{tax_value};
354 $foot{$$line{tax_rate}}{quantity} += get_rounded_price($$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;
381 foreach my $r (@{$budgets}) {
382 next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
389 basketno => $basketno,
391 basketname => $basket->{'basketname'},
392 basketbranchcode => $basket->{branch},
393 basketnote => $basket->{note},
394 basketbooksellernote => $basket->{booksellernote},
395 basketcontractno => $basket->{contractnumber},
396 basketcontractname => $contract->{contractname},
397 branches_loop => \@branches_loop,
398 creationdate => $basket->{creationdate},
399 authorisedby => $basket->{authorisedby},
400 authorisedbyname => $basket->{authorisedbyname},
401 users_ids => join(':', @basketusers_ids),
402 users => \@basketusers,
403 closedate => $basket->{closedate},
404 estimateddeliverydate=> $estimateddeliverydate,
405 is_standing => $basket->{is_standing},
406 deliveryplace => $basket->{deliveryplace},
407 billingplace => $basket->{billingplace},
408 active => $bookseller->active,
409 booksellerid => $bookseller->id,
410 booksellername => $bookseller->name,
411 books_loop => \@books_loop,
412 book_foot_loop => \@book_foot_loop,
413 cancelledorders_loop => \@cancelledorders_loop,
414 total_quantity => $total_quantity,
415 total_tax_excluded => $total_tax_excluded,
416 total_tax_included => $total_tax_included,
417 total_tax_value => $total_tax_value,
418 currency => $active_currency->currency,
419 listincgst => $bookseller->listincgst,
420 basketgroups => $basketgroups,
421 basketgroup => $basketgroup,
422 grouped => $basket->{basketgroupid},
423 # The double negatives and booleans here mean:
424 # "A basket cannot be closed if there are no orders in it or it's a standing order basket."
426 # (The template has another implicit restriction that the order cannot be closed if there
427 # are any orders with uncertain prices.)
428 unclosable => @orders ? $basket->{is_standing} : 1,
429 has_budgets => $has_budgets,
430 duplinbatch => $duplinbatch,
431 csv_profiles => [ Koha::CsvProfiles->search({ type => 'sql', used_for => 'export_basket' }) ],
432 available_additional_fields => [ Koha::AdditionalFields->search( { tablename => 'aqbasket' } ) ],
433 additional_field_values => { map {
434 $_->field->name => $_->value
435 } Koha::Acquisition::Baskets->find($basketno)->additional_field_values->as_list },
439 $template->param( messages => \@messages );
440 output_html_with_http_headers $query, $cookie, $template->output;
442 sub get_order_infos {
444 my $bookseller = shift;
445 my $qty = $order->{'quantity'} || 0;
446 if ( !defined $order->{quantityreceived} ) {
447 $order->{quantityreceived} = 0;
449 my $budget = GetBudget($order->{budget_id});
450 my $basket = GetBasket($order->{basketno});
452 my %line = %{ $order };
453 # Don't show unreceived standing orders as received
454 $line{order_received} = ( $qty == $order->{'quantityreceived'} && ( $basket->{is_standing} ? $qty : 1 ) );
455 $line{basketno} = $basketno;
456 $line{budget_name} = $budget->{budget_name};
458 # If we have an actual cost that should be the total, otherwise use the ecost
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 );