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 my $logged_in_patron = Koha::Patrons->find( $loggedinuser );
96 our $basket = GetBasket($basketno);
97 $booksellerid = $basket->{booksellerid} unless $booksellerid;
98 my $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
99 my $schema = Koha::Database->new()->schema();
100 my $rs = $schema->resultset('VendorEdiAccount')->search(
101 { vendor_id => $booksellerid, } );
102 $template->param( ediaccount => ($rs->count > 0));
104 unless (CanUserManageBasket($loggedinuser, $basket, $userflags)) {
106 cannot_manage_basket => 1,
107 basketno => $basketno,
108 basketname => $basket->{basketname},
109 booksellerid => $booksellerid,
110 booksellername => $bookseller->name,
112 output_html_with_http_headers $query, $cookie, $template->output;
116 # FIXME : what about the "discount" percentage?
117 # FIXME : the query->param('booksellerid') below is probably useless. The bookseller is always known from the basket
118 # if no booksellerid in parameter, get it from basket
119 # warn "=>".$basket->{booksellerid};
120 my $op = $query->param('op') // 'list';
122 our $confirm_pref= C4::Context->preference("BasketConfirmations") || '1';
123 $template->param( skip_confirm_reopen => 1) if $confirm_pref eq '2';
127 if ( $op eq 'delete_confirm' ) {
129 output_and_exit( $query, $cookie, $template, 'insufficient_permission' )
130 unless $logged_in_patron->has_permission( { acquisition => 'delete_baskets' } );
132 my $basketno = $query->param('basketno');
133 my $delbiblio = $query->param('delbiblio');
134 my @orders = GetOrders($basketno);
135 #Delete all orders included in that basket, and all items received.
136 foreach my $myorder (@orders){
137 DelOrder($myorder->{biblionumber},$myorder->{ordernumber});
139 # if $delbiblio = 1, delete the records if possible
140 if ((defined $delbiblio)and ($delbiblio ==1)){
141 my @cannotdelbiblios ;
142 foreach my $myorder (@orders){
143 my $biblionumber = $myorder->{'biblionumber'};
144 my $biblio = Koha::Biblios->find( $biblionumber );
145 my $countbiblio = $biblio->active_orders->count;
146 my $ordernumber = $myorder->{'ordernumber'};
147 my $cnt_subscriptions = $biblio->subscriptions->count;
148 my $itemcount = $biblio->items->count;
150 if ($countbiblio == 0 && $itemcount == 0 && not $cnt_subscriptions ) {
151 $error = DelBiblio($myorder->{biblionumber}) }
153 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
154 title=> $myorder->{'title'},
155 author=> $myorder->{'author'},
156 countbiblio=> $countbiblio,
157 itemcount=>$itemcount,
158 subscriptions => $cnt_subscriptions};
161 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
162 title=> $myorder->{'title'},
163 author=> $myorder->{'author'},
164 othererror=> $error};
167 $template->param( cannotdelbiblios => \@cannotdelbiblios );
170 DelBasket($basketno,);
172 delete_confirmed => 1,
173 booksellername => $bookseller->name,
174 booksellerid => $booksellerid,
176 } elsif ( !$bookseller ) {
177 $template->param( NO_BOOKSELLER => 1 );
178 } elsif ($op eq 'export') {
179 print $query->header(
181 -attachment => 'basket' . $basket->{'basketno'} . '.csv',
183 my $csv_profile_id = $query->param('csv_profile');
184 print GetBasketAsCSV( scalar $query->param('basketno'), $query, $csv_profile_id ); # if no csv_profile_id passed, using default rows
186 } elsif ($op eq 'email') {
188 SendAlerts( 'orderacquisition', $query->param('basketno'), 'ACQORDER' );
191 push @messages, { type => 'error', code => $@ };
192 } elsif ( ref $err and exists $err->{error} ) {
193 push @messages, { type => 'error', code => $err->{error} };
195 push @messages, { type => 'message', code => 'email_sent' };
199 } elsif ($op eq 'close') {
200 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
202 my $basketno = $query->param('basketno');
203 my $booksellerid = $query->param('booksellerid');
204 $basketno =~ /^\d+$/ and CloseBasket($basketno);
205 # if requested, create basket group, close it and attach the basket
206 if ($query->param('createbasketgroup')) {
208 if(C4::Context->userenv and C4::Context->userenv->{'branch'}) {
209 $branchcode = C4::Context->userenv->{'branch'};
211 my $basketgroupid = NewBasketgroup( { name => $basket->{basketname},
212 booksellerid => $booksellerid,
213 deliveryplace => $branchcode,
214 billingplace => $branchcode,
217 ModBasket( { basketno => $basketno,
218 basketgroupid => $basketgroupid } );
219 print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
221 print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
226 confirm_close => "1",
227 booksellerid => $booksellerid,
228 booksellername => $bookseller->name,
229 basketno => $basket->{'basketno'},
230 basketname => $basket->{'basketname'},
231 basketgroupname => $basket->{'basketname'},
234 } elsif ($op eq 'reopen') {
235 ReopenBasket(scalar $query->param('basketno'));
236 print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
238 elsif ( $op eq 'ediorder' ) {
239 edi_close_and_order()
240 } elsif ( $op eq 'mod_users' ) {
241 my $basketusers_ids = $query->param('users_ids');
242 my @basketusers = split( /:/, $basketusers_ids );
243 ModBasketUsers($basketno, @basketusers);
244 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
246 } elsif ( $op eq 'mod_branch' ) {
247 my $branch = $query->param('branch');
248 $branch = undef if(defined $branch and $branch eq '');
250 basketno => $basket->{basketno},
253 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
257 if ( $op eq 'list' ) {
259 # get librarian branch...
260 if ( C4::Context->preference("IndependentBranches") ) {
261 my $userenv = C4::Context->userenv;
262 unless ( C4::Context->IsSuperLibrarian() ) {
263 my $validtest = ( $basket->{creationdate} eq '' )
264 || ( $userenv->{branch} eq $basket->{branch} )
265 || ( $userenv->{branch} eq '' )
266 || ( $basket->{branch} eq '' );
267 unless ($validtest) {
268 print $query->redirect("../mainpage.pl");
273 if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
274 push @branches_loop, {
275 branchcode => $userenv->{branch},
276 branchname => $userenv->{branchname},
282 my $branches = Koha::Libraries->search( {}, { order_by => ['branchname'] } )->unblessed;
283 foreach my $branch (@$branches) {
285 if (defined $basket->{branch}) {
286 $selected = 1 if $branch->{branchcode} eq $basket->{branch};
288 $selected = 1 if $branch->{branchcode} eq C4::Context->userenv->{branch};
290 push @branches_loop, {
291 branchcode => $branch->{branchcode},
292 branchname => $branch->{branchname},
293 selected => $selected
298 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
299 my ($basketgroup, $basketgroups);
300 my $patron = Koha::Patrons->find($loggedinuser);
301 if ($basket->{closedate} && haspermission($patron->userid, { acquisition => 'group_manage'} )) {
302 $basketgroups = GetBasketgroups($basket->{booksellerid});
303 for my $bg ( @{$basketgroups} ) {
304 if ($basket->{basketgroupid} && $basket->{basketgroupid} == $bg->{id}){
311 # if the basket is closed, calculate estimated delivery date
312 my $estimateddeliverydate;
313 if( $basket->{closedate} ) {
314 my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
315 ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->deliverytime);
316 $estimateddeliverydate = sprintf( "%04d-%02d-%02d", $year, $month, $day );
319 # if new basket, pre-fill infos
320 $basket->{creationdate} = "" unless ( $basket->{creationdate} );
321 $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
324 "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
325 $basket->{creationdate}, $basket->{authorisedby};
327 my @basketusers_ids = GetBasketUsers($basketno);
329 foreach my $basketuser_id (@basketusers_ids) {
330 # FIXME Could be improved with a search -in
331 my $basket_patron = Koha::Patrons->find( $basketuser_id );
332 push @basketusers, $basket_patron if $basket_patron;
335 my $active_currency = Koha::Acquisition::Currencies->get_active;
337 my @orders = GetOrders( $basketno );
342 my $total_quantity = 0;
343 my $total_tax_excluded = 0;
344 my $total_tax_included = 0;
345 my $total_tax_value = 0;
346 for my $order (@orders) {
347 my $line = get_order_infos( $order, $bookseller);
348 if ( $line->{uncertainprice} ) {
349 $template->param( uncertainprices => 1 );
352 $line->{tax_rate} = $line->{tax_rate_on_ordering} // 0;
353 $line->{tax_value} = $line->{tax_value_on_ordering} // 0;
355 push @books_loop, $line;
357 $foot{$$line{tax_rate}}{tax_rate} = $$line{tax_rate};
358 $foot{$$line{tax_rate}}{tax_value} += get_rounded_price($$line{tax_value});
359 $total_tax_value += $$line{tax_value};
360 $foot{$$line{tax_rate}}{quantity} += get_rounded_price($$line{quantity});
361 $total_quantity += $$line{quantity};
362 $foot{$$line{tax_rate}}{total_tax_excluded} += $$line{total_tax_excluded};
363 $total_tax_excluded += $$line{total_tax_excluded};
364 $foot{$$line{tax_rate}}{total_tax_included} += $$line{total_tax_included};
365 $total_tax_included += $$line{total_tax_included};
368 push @book_foot_loop, map {$_} values %foot;
370 # Get cancelled orders
371 my @cancelledorders = GetOrders($basketno, { cancelled => 1 });
372 my @cancelledorders_loop;
373 for my $order (@cancelledorders) {
374 my $line = get_order_infos( $order, $bookseller);
375 push @cancelledorders_loop, $line;
378 my $contract = GetContract({
379 contractnumber => $basket->{contractnumber}
382 if ($basket->{basketgroupid}){
383 $basketgroup = GetBasketgroup($basket->{basketgroupid});
385 my $budgets = GetBudgetHierarchy;
387 foreach my $r (@{$budgets}) {
388 if (!defined $r->{budget_amount} || $r->{budget_amount} == 0) {
391 next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
398 basketno => $basketno,
400 basketname => $basket->{'basketname'},
401 basketbranchcode => $basket->{branch},
402 basketnote => $basket->{note},
403 basketbooksellernote => $basket->{booksellernote},
404 basketcontractno => $basket->{contractnumber},
405 basketcontractname => $contract->{contractname},
406 branches_loop => \@branches_loop,
407 creationdate => $basket->{creationdate},
408 authorisedby => $basket->{authorisedby},
409 authorisedbyname => $basket->{authorisedbyname},
410 users_ids => join(':', @basketusers_ids),
411 users => \@basketusers,
412 closedate => $basket->{closedate},
413 estimateddeliverydate=> $estimateddeliverydate,
414 is_standing => $basket->{is_standing},
415 deliveryplace => $basket->{deliveryplace},
416 billingplace => $basket->{billingplace},
417 active => $bookseller->active,
418 booksellerid => $bookseller->id,
419 booksellername => $bookseller->name,
420 books_loop => \@books_loop,
421 book_foot_loop => \@book_foot_loop,
422 cancelledorders_loop => \@cancelledorders_loop,
423 total_quantity => $total_quantity,
424 total_tax_excluded => $total_tax_excluded,
425 total_tax_included => $total_tax_included,
426 total_tax_value => $total_tax_value,
427 currency => $active_currency->currency,
428 listincgst => $bookseller->listincgst,
429 basketgroups => $basketgroups,
430 basketgroup => $basketgroup,
431 grouped => $basket->{basketgroupid},
432 # The double negatives and booleans here mean:
433 # "A basket cannot be closed if there are no orders in it or it's a standing order basket."
435 # (The template has another implicit restriction that the order cannot be closed if there
436 # are any orders with uncertain prices.)
437 unclosable => @orders ? $basket->{is_standing} : 1,
438 has_budgets => $has_budgets,
439 duplinbatch => $duplinbatch,
440 csv_profiles => [ Koha::CsvProfiles->search({ type => 'sql', used_for => 'export_basket' }) ],
441 available_additional_fields => [ Koha::AdditionalFields->search( { tablename => 'aqbasket' } ) ],
442 additional_field_values => { map {
443 $_->field->name => $_->value
444 } Koha::Acquisition::Baskets->find($basketno)->additional_field_values->as_list },
448 $template->param( messages => \@messages );
449 output_html_with_http_headers $query, $cookie, $template->output;
451 sub get_order_infos {
453 my $bookseller = shift;
454 my $qty = $order->{'quantity'} || 0;
455 if ( !defined $order->{quantityreceived} ) {
456 $order->{quantityreceived} = 0;
458 my $budget = GetBudget($order->{budget_id});
459 my $basket = GetBasket($order->{basketno});
461 my %line = %{ $order };
462 # Don't show unreceived standing orders as received
463 $line{order_received} = ( $qty == $order->{'quantityreceived'} && ( $basket->{is_standing} ? $qty : 1 ) );
464 $line{basketno} = $basketno;
465 $line{budget_name} = $budget->{budget_name};
467 # If we have an actual cost that should be the total, otherwise use the ecost
468 $line{unitprice_tax_included} += 0;
469 $line{unitprice_tax_excluded} += 0;
470 my $cost_tax_included = $line{unitprice_tax_included} || $line{ecost_tax_included};
471 my $cost_tax_excluded = $line{unitprice_tax_excluded} || $line{ecost_tax_excluded};
472 $line{total_tax_included} = get_rounded_price($cost_tax_included) * $line{quantity};
473 $line{total_tax_excluded} = get_rounded_price($cost_tax_excluded) * $line{quantity};
474 $line{tax_value} = $line{tax_value_on_ordering};
475 $line{tax_rate} = $line{tax_rate_on_ordering};
477 if ( $line{'title'} ) {
478 my $volume = $order->{'volume'};
479 my $seriestitle = $order->{'seriestitle'};
480 $line{'title'} .= " / $seriestitle" if $seriestitle;
481 $line{'title'} .= " / $volume" if $volume;
484 my $biblionumber = $order->{'biblionumber'};
485 if ( $biblionumber ) { # The biblio still exists
486 my $biblio = Koha::Biblios->find( $biblionumber );
487 my $countbiblio = $biblio->active_orders->count;
489 my $ordernumber = $order->{'ordernumber'};
490 my $cnt_subscriptions = $biblio->subscriptions->count;
491 my $itemcount = $biblio->items->count;
492 my $holds_count = $biblio->holds->count;
493 my $order = Koha::Acquisition::Orders->find($ordernumber); # FIXME We should certainly do that at the beginning of this sub
494 my $items = $order->items;
495 my $itemholds = $biblio->holds->search({ itemnumber => { -in => [ $items->get_column('itemnumber') ] } })->count;
497 # 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
498 $line{can_del_bib} = 1 if $countbiblio <= 1 && $itemcount == $items->count && !($cnt_subscriptions) && !($holds_count);
499 $line{items} = $itemcount - $items->count;
500 $line{left_item} = 1 if $line{items} >= 1;
501 $line{left_biblio} = 1 if $countbiblio > 1;
502 $line{biblios} = $countbiblio - 1;
503 $line{left_subscription} = 1 if $cnt_subscriptions;
504 $line{subscriptions} = $cnt_subscriptions;
505 ($holds_count >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
506 $line{left_holds_on_order} = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
507 $line{holds} = $holds_count;
508 $line{holds_on_order} = $itemholds?$itemholds:$holds_count if $line{left_holds_on_order};
509 $line{order_object} = $order;
513 my $suggestion = GetSuggestionInfoFromBiblionumber($line{biblionumber});
514 $line{suggestionid} = $$suggestion{suggestionid};
515 $line{surnamesuggestedby} = $$suggestion{surnamesuggestedby};
516 $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
518 foreach my $key (qw(transferred_from transferred_to)) {
520 my $order = GetOrder($line{$key});
521 my $basket = GetBasket($order->{basketno});
522 my $bookseller = Koha::Acquisition::Booksellers->find( $basket->{booksellerid} );
526 bookseller => $bookseller,
527 timestamp => $line{$key . '_timestamp'},
535 sub edi_close_and_order {
536 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
539 basketno => $basketno,
542 if ( $basket->{branch} ) {
543 $edi_params->{branchcode} = $basket->{branch};
545 if ( create_edi_order($edi_params) ) {
546 #$template->param( edifile => 1 );
548 CloseBasket($basketno);
550 # if requested, create basket group, close it and attach the basket
551 if ( $query->param('createbasketgroup') ) {
553 if ( C4::Context->userenv
554 and C4::Context->userenv->{'branch'} )
556 $branchcode = C4::Context->userenv->{'branch'};
558 my $basketgroupid = NewBasketgroup(
560 name => $basket->{basketname},
561 booksellerid => $booksellerid,
562 deliveryplace => $branchcode,
563 billingplace => $branchcode,
569 basketno => $basketno,
570 basketgroupid => $basketgroupid
573 print $query->redirect(
574 "/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=$booksellerid&closed=1"
578 print $query->redirect(
579 "/cgi-bin/koha/acqui/booksellers.pl?booksellerid=$booksellerid"
587 booksellerid => $booksellerid,
588 basketno => $basket->{basketno},
589 basketname => $basket->{basketname},
590 basketgroupname => $basket->{basketname},
593 $template->param( ean => $ean );