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>.
24 use C4::Auth qw( get_template_and_user haspermission );
25 use C4::Output qw( output_html_with_http_headers output_and_exit );
27 use C4::Acquisition qw( GetBasket CanUserManageBasket GetBasketAsCSV NewBasket NewBasketgroup ModBasket ReopenBasket ModBasketUsers GetBasketgroup GetBasketgroups GetBasketUsers GetOrders GetOrder get_rounded_price );
28 use C4::Budgets qw( GetBudgetHierarchy GetBudget CanUserUseBudget );
29 use C4::Contract qw( GetContract );
30 use C4::Suggestions qw( GetSuggestion GetSuggestionInfoFromBiblionumber GetSuggestionInfo );
32 use Koha::Acquisition::Baskets;
33 use Koha::Acquisition::Booksellers;
34 use Koha::Acquisition::Orders;
36 use C4::Letters qw( SendAlerts );
37 use Date::Calc qw( Add_Delta_Days );
39 use Koha::EDI qw( create_edi_order );
40 use Koha::CsvProfiles;
43 use Koha::AdditionalFields;
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.
73 our $query = CGI->new;
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",
84 flagsrequired => { acquisition => 'order_manage' },
88 my $logged_in_patron = Koha::Patrons->find( $loggedinuser );
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' ) {
123 output_and_exit( $query, $cookie, $template, 'insufficient_permission' )
124 unless $logged_in_patron->has_permission( { acquisition => 'delete_baskets' } );
126 my $basketno = $query->param('basketno');
127 my $delbiblio = $query->param('delbiblio');
128 my $basket_obj = Koha::Acquisition::Baskets->find($basketno);
130 my $orders = $basket_obj->orders->filter_by_current;
132 my @cannotdelbiblios;
134 while ( my $order = $orders->next ) {
136 $order->cancel({ delete_biblio => $delbiblio });
137 my @messages = @{ $order->object_messages };
139 if ( scalar @messages > 0 ) {
141 my $biblio = $order->biblio;
143 push @cannotdelbiblios, {
144 biblionumber => $biblio->id,
145 title => $biblio->title // '',
146 author => $biblio->author // '',
147 countbiblio => $biblio->active_orders->count,
148 itemcount => $biblio->items->count,
149 subscriptions => $biblio->subscriptions->count,
154 $template->param( cannotdelbiblios => \@cannotdelbiblios );
159 delete_confirmed => 1,
160 booksellername => $bookseller->name,
161 booksellerid => $booksellerid,
163 } elsif ( !$bookseller ) {
164 $template->param( NO_BOOKSELLER => 1 );
165 } elsif ($op eq 'export') {
166 print $query->header(
168 -attachment => 'basket' . $basket->{'basketno'} . '.csv',
170 my $csv_profile_id = $query->param('csv_profile');
171 print GetBasketAsCSV( scalar $query->param('basketno'), $query, $csv_profile_id ); # if no csv_profile_id passed, using default rows
173 } elsif ($op eq 'email') {
175 SendAlerts( 'orderacquisition', scalar $query->param('basketno'), 'ACQORDER' );
178 push @messages, { type => 'error', code => $@ };
179 } elsif ( ref $err and exists $err->{error} ) {
180 push @messages, { type => 'error', code => $err->{error} };
182 push @messages, { type => 'message', code => 'email_sent' };
186 } elsif ($op eq 'close') {
187 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
191 # FIXME: we should fetch the object at the beginning of this script
192 # and get rid of the hash that is passed around
193 Koha::Acquisition::Baskets->find($basketno)->close;
195 # if requested, create basket group, close it and attach the basket
196 if ($query->param('createbasketgroup')) {
198 if(C4::Context->userenv and C4::Context->userenv->{'branch'}) {
199 $branchcode = C4::Context->userenv->{'branch'};
201 my $basketgroupid = NewBasketgroup( { name => $basket->{basketname},
202 booksellerid => $booksellerid,
203 deliveryplace => $branchcode,
204 billingplace => $branchcode,
207 ModBasket( { basketno => $basketno,
208 basketgroupid => $basketgroupid } );
209 print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
211 print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
216 confirm_close => "1",
217 booksellerid => $booksellerid,
218 booksellername => $bookseller->name,
219 basketno => $basket->{'basketno'},
220 basketname => $basket->{'basketname'},
221 basketgroupname => $basket->{'basketname'},
224 } elsif ($op eq 'reopen') {
225 ReopenBasket(scalar $query->param('basketno'));
226 print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
228 elsif ( $op eq 'ediorder' ) {
229 edi_close_and_order()
230 } elsif ( $op eq 'mod_users' ) {
231 my $basketusers_ids = $query->param('users_ids');
232 my @basketusers = split( /:/, $basketusers_ids );
233 ModBasketUsers($basketno, @basketusers);
234 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
236 } elsif ( $op eq 'mod_branch' ) {
237 my $branch = $query->param('branch');
238 $branch = undef if(defined $branch and $branch eq '');
240 basketno => $basket->{basketno},
243 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
247 if ( $op eq 'list' ) {
249 # get librarian branch...
250 if ( C4::Context->preference("IndependentBranches") ) {
251 my $userenv = C4::Context->userenv;
252 unless ( C4::Context->IsSuperLibrarian() ) {
253 my $validtest = ( $basket->{creationdate} eq '' )
254 || ( $userenv->{branch} eq $basket->{branch} )
255 || ( $userenv->{branch} eq '' )
256 || ( $basket->{branch} eq '' );
257 unless ($validtest) {
258 print $query->redirect("../mainpage.pl");
263 if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
264 push @branches_loop, {
265 branchcode => $userenv->{branch},
266 branchname => $userenv->{branchname},
272 my $branches = Koha::Libraries->search( {}, { order_by => ['branchname'] } )->unblessed;
273 foreach my $branch (@$branches) {
275 if (defined $basket->{branch}) {
276 $selected = 1 if $branch->{branchcode} eq $basket->{branch};
278 $selected = 1 if $branch->{branchcode} eq C4::Context->userenv->{branch};
280 push @branches_loop, {
281 branchcode => $branch->{branchcode},
282 branchname => $branch->{branchname},
283 selected => $selected
288 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
289 my ($basketgroup, $basketgroups);
290 my $patron = Koha::Patrons->find($loggedinuser);
291 if ($basket->{closedate} && haspermission($patron->userid, { acquisition => 'group_manage'} )) {
292 $basketgroups = GetBasketgroups($basket->{booksellerid});
293 for my $bg ( @{$basketgroups} ) {
294 if ($basket->{basketgroupid} && $basket->{basketgroupid} == $bg->{id}){
301 # if the basket is closed, calculate estimated delivery date
302 my $estimateddeliverydate;
303 if( $basket->{closedate} ) {
304 my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
305 ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->deliverytime);
306 $estimateddeliverydate = sprintf( "%04d-%02d-%02d", $year, $month, $day );
309 # if new basket, pre-fill infos
310 $basket->{creationdate} = "" unless ( $basket->{creationdate} );
311 $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
313 my @basketusers_ids = GetBasketUsers($basketno);
315 foreach my $basketuser_id (@basketusers_ids) {
316 # FIXME Could be improved with a search -in
317 my $basket_patron = Koha::Patrons->find( $basketuser_id );
318 push @basketusers, $basket_patron if $basket_patron;
321 my $active_currency = Koha::Acquisition::Currencies->get_active;
323 my @orders = GetOrders( $basketno );
328 my $total_quantity = 0;
329 my $total_tax_excluded = 0;
330 my $total_tax_included = 0;
331 my $total_tax_value = 0;
332 for my $order (@orders) {
333 my $line = get_order_infos( $order, $bookseller);
334 if ( $line->{uncertainprice} ) {
335 $template->param( uncertainprices => 1 );
338 $line->{tax_rate} = $line->{tax_rate_on_ordering} // 0;
339 $line->{tax_value} = $line->{tax_value_on_ordering} // 0;
341 push @books_loop, $line;
343 $foot{$$line{tax_rate}}{tax_rate} = $$line{tax_rate};
344 $foot{$$line{tax_rate}}{tax_value} += get_rounded_price($$line{tax_value});
345 $total_tax_value += $$line{tax_value};
346 $foot{$$line{tax_rate}}{quantity} += get_rounded_price($$line{quantity});
347 $total_quantity += $$line{quantity};
348 $foot{$$line{tax_rate}}{total_tax_excluded} += $$line{total_tax_excluded};
349 $total_tax_excluded += $$line{total_tax_excluded};
350 $foot{$$line{tax_rate}}{total_tax_included} += $$line{total_tax_included};
351 $total_tax_included += $$line{total_tax_included};
354 push @book_foot_loop, map {$_} values %foot;
356 # Get cancelled orders
357 my @cancelledorders = GetOrders($basketno, { cancelled => 1 });
358 my @cancelledorders_loop;
359 for my $order (@cancelledorders) {
360 my $line = get_order_infos( $order, $bookseller);
361 push @cancelledorders_loop, $line;
364 my $contract = GetContract({
365 contractnumber => $basket->{contractnumber}
368 if ($basket->{basketgroupid}){
369 $basketgroup = GetBasketgroup($basket->{basketgroupid});
371 my $budgets = GetBudgetHierarchy;
373 foreach my $r (@{$budgets}) {
374 next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
380 my $basket_obj = Koha::Acquisition::Baskets->find($basketno);
381 my $edi_order = $basket_obj->edi_order;
384 basketno => $basketno,
386 basketname => $basket->{'basketname'},
387 basketbranchcode => $basket->{branch},
388 basketnote => $basket->{note},
389 basketbooksellernote => $basket->{booksellernote},
390 basketcontractno => $basket->{contractnumber},
391 basketcontractname => $contract->{contractname},
392 branches_loop => \@branches_loop,
393 creationdate => $basket->{creationdate},
394 edi_order => $edi_order,
395 authorisedby => $basket->{authorisedby},
396 authorisedbyname => $basket->{authorisedbyname},
397 users_ids => join(':', @basketusers_ids),
398 users => \@basketusers,
399 closedate => $basket->{closedate},
400 estimateddeliverydate=> $estimateddeliverydate,
401 is_standing => $basket->{is_standing},
402 deliveryplace => $basket->{deliveryplace},
403 billingplace => $basket->{billingplace},
404 active => $bookseller->active,
405 booksellerid => $bookseller->id,
406 booksellername => $bookseller->name,
407 books_loop => \@books_loop,
408 book_foot_loop => \@book_foot_loop,
409 cancelledorders_loop => \@cancelledorders_loop,
410 total_quantity => $total_quantity,
411 total_tax_excluded => $total_tax_excluded,
412 total_tax_included => $total_tax_included,
413 total_tax_value => $total_tax_value,
414 currency => $active_currency->currency,
415 listincgst => $bookseller->listincgst,
416 basketgroups => $basketgroups,
417 basketgroup => $basketgroup,
418 grouped => $basket->{basketgroupid},
419 # The double negatives and booleans here mean:
420 # "A basket cannot be closed if there are no orders in it or it's a standing order basket."
422 # (The template has another implicit restriction that the order cannot be closed if there
423 # are any orders with uncertain prices.)
424 unclosable => @orders || @cancelledorders ? $basket->{is_standing} : 1,
425 has_budgets => $has_budgets,
426 duplinbatch => $duplinbatch,
427 csv_profiles => Koha::CsvProfiles->search({ type => 'sql', used_for => 'export_basket' }),
428 available_additional_fields => Koha::AdditionalFields->search( { tablename => 'aqbasket' } ),
429 additional_field_values => { map {
430 $_->field->name => $_->value
431 } Koha::Acquisition::Baskets->find($basketno)->additional_field_values->as_list },
435 $template->param( messages => \@messages );
436 output_html_with_http_headers $query, $cookie, $template->output;
438 sub get_order_infos {
440 my $bookseller = shift;
441 my $qty = $order->{'quantity'} || 0;
442 if ( !defined $order->{quantityreceived} ) {
443 $order->{quantityreceived} = 0;
445 my $budget = GetBudget($order->{budget_id});
446 my $basket = GetBasket($order->{basketno});
448 my %line = %{ $order };
449 # Don't show unreceived standing orders as received
450 $line{order_received} = ( $qty == $order->{'quantityreceived'} && ( $basket->{is_standing} ? $qty : 1 ) );
451 $line{basketno} = $basketno;
452 $line{budget_name} = $budget->{budget_name};
454 # If we have an actual cost that should be the total, otherwise use the ecost
455 $line{unitprice_tax_included} += 0;
456 $line{unitprice_tax_excluded} += 0;
457 my $cost_tax_included = $line{unitprice_tax_included} || $line{ecost_tax_included};
458 my $cost_tax_excluded = $line{unitprice_tax_excluded} || $line{ecost_tax_excluded};
459 $line{total_tax_included} = get_rounded_price($cost_tax_included) * $line{quantity};
460 $line{total_tax_excluded} = get_rounded_price($cost_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{'title'} ) {
465 my $volume = $order->{'volume'};
466 my $seriestitle = $order->{'seriestitle'};
467 $line{'title'} .= " / $seriestitle" if $seriestitle;
468 $line{'title'} .= " / $volume" if $volume;
471 my $biblionumber = $order->{'biblionumber'};
472 if ( $biblionumber ) { # The biblio still exists
473 my $biblio = Koha::Biblios->find( $biblionumber );
474 my $countbiblio = $biblio->active_orders->count;
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 $order = Koha::Acquisition::Orders->find($ordernumber); # FIXME We should certainly do that at the beginning of this sub
481 my $items = $order->items;
482 my $itemholds = $biblio->holds->search({ itemnumber => { -in => [ $items->get_column('itemnumber') ] } })->count;
484 # 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
485 $line{can_del_bib} = 1 if $countbiblio <= 1 && $itemcount == $items->count && !($cnt_subscriptions) && !($holds_count);
486 $line{items} = $itemcount - $items->count;
487 $line{left_item} = 1 if $line{items} >= 1;
488 $line{left_biblio} = 1 if $countbiblio > 1;
489 $line{biblios} = $countbiblio - 1;
490 $line{left_subscription} = 1 if $cnt_subscriptions;
491 $line{subscriptions} = $cnt_subscriptions;
492 ($holds_count >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
493 $line{left_holds_on_order} = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
494 $line{holds} = $holds_count;
495 $line{holds_on_order} = $itemholds?$itemholds:$holds_count if $line{left_holds_on_order};
496 $line{order_object} = $order;
499 my $suggestion = GetSuggestionInfoFromBiblionumber($line{biblionumber});
500 $line{suggestionid} = $$suggestion{suggestionid};
501 $line{surnamesuggestedby} = $$suggestion{surnamesuggestedby};
502 $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
504 $line{estimated_delivery_date} = $order->{estimated_delivery_date};
506 foreach my $key (qw(transferred_from transferred_to)) {
508 my $order = GetOrder($line{$key});
509 my $basket = GetBasket($order->{basketno});
510 my $bookseller = Koha::Acquisition::Booksellers->find( $basket->{booksellerid} );
514 bookseller => $bookseller,
515 timestamp => $line{$key . '_timestamp'},
523 sub edi_close_and_order {
524 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
527 basketno => $basketno,
530 if ( $basket->{branch} ) {
531 $edi_params->{branchcode} = $basket->{branch};
533 if ( create_edi_order($edi_params) ) {
534 #$template->param( edifile => 1 );
536 Koha::Acquisition::Baskets->find($basketno)->close;
538 # if requested, create basket group, close it and attach the basket
539 if ( $query->param('createbasketgroup') ) {
541 if ( C4::Context->userenv
542 and C4::Context->userenv->{'branch'} )
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 );