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",
88 flagsrequired => { acquisition => 'order_manage' },
93 our $basket = GetBasket($basketno);
94 $booksellerid = $basket->{booksellerid} unless $booksellerid;
95 my $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
96 my $schema = Koha::Database->new()->schema();
97 my $rs = $schema->resultset('VendorEdiAccount')->search(
98 { vendor_id => $booksellerid, } );
99 $template->param( ediaccount => ($rs->count > 0));
101 unless (CanUserManageBasket($loggedinuser, $basket, $userflags)) {
103 cannot_manage_basket => 1,
104 basketno => $basketno,
105 basketname => $basket->{basketname},
106 booksellerid => $booksellerid,
107 booksellername => $bookseller->name,
109 output_html_with_http_headers $query, $cookie, $template->output;
113 # FIXME : what about the "discount" percentage?
114 # FIXME : the query->param('booksellerid') below is probably useless. The bookseller is always known from the basket
115 # if no booksellerid in parameter, get it from basket
116 # warn "=>".$basket->{booksellerid};
117 my $op = $query->param('op') // 'list';
119 our $confirm_pref= C4::Context->preference("BasketConfirmations") || '1';
120 $template->param( skip_confirm_reopen => 1) if $confirm_pref eq '2';
124 if ( $op eq 'delete_confirm' ) {
125 my $basketno = $query->param('basketno');
126 my $delbiblio = $query->param('delbiblio');
127 my @orders = GetOrders($basketno);
128 #Delete all orders included in that basket, and all items received.
129 foreach my $myorder (@orders){
130 DelOrder($myorder->{biblionumber},$myorder->{ordernumber});
132 # if $delbiblio = 1, delete the records if possible
133 if ((defined $delbiblio)and ($delbiblio ==1)){
134 my @cannotdelbiblios ;
135 foreach my $myorder (@orders){
136 my $biblionumber = $myorder->{'biblionumber'};
137 my $biblio = Koha::Biblios->find( $biblionumber );
138 my $countbiblio = $biblio->active_orders->count;
139 my $ordernumber = $myorder->{'ordernumber'};
140 my $cnt_subscriptions = $biblio->subscriptions->count;
141 my $itemcount = $biblio->items->count;
143 if ($countbiblio == 0 && $itemcount == 0 && not $cnt_subscriptions ) {
144 $error = DelBiblio($myorder->{biblionumber}) }
146 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
147 title=> $myorder->{'title'},
148 author=> $myorder->{'author'},
149 countbiblio=> $countbiblio,
150 itemcount=>$itemcount,
151 subscriptions => $cnt_subscriptions};
154 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
155 title=> $myorder->{'title'},
156 author=> $myorder->{'author'},
157 othererror=> $error};
160 $template->param( cannotdelbiblios => \@cannotdelbiblios );
163 DelBasket($basketno,);
165 delete_confirmed => 1,
166 booksellername => $bookseller->name,
167 booksellerid => $booksellerid,
169 } elsif ( !$bookseller ) {
170 $template->param( NO_BOOKSELLER => 1 );
171 } elsif ($op eq 'export') {
172 print $query->header(
174 -attachment => 'basket' . $basket->{'basketno'} . '.csv',
176 my $csv_profile_id = $query->param('csv_profile');
177 print GetBasketAsCSV( scalar $query->param('basketno'), $query, $csv_profile_id ); # if no csv_profile_id passed, using default rows
179 } elsif ($op eq 'email') {
181 SendAlerts( 'orderacquisition', $query->param('basketno'), 'ACQORDER' );
184 push @messages, { type => 'error', code => $@ };
185 } elsif ( ref $err and exists $err->{error} ) {
186 push @messages, { type => 'error', code => $err->{error} };
188 push @messages, { type => 'message', code => 'email_sent' };
192 } elsif ($op eq 'close') {
193 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
195 my $basketno = $query->param('basketno');
196 my $booksellerid = $query->param('booksellerid');
197 $basketno =~ /^\d+$/ and CloseBasket($basketno);
198 # if requested, create basket group, close it and attach the basket
199 if ($query->param('createbasketgroup')) {
201 if(C4::Context->userenv and C4::Context->userenv->{'branch'}) {
202 $branchcode = C4::Context->userenv->{'branch'};
204 my $basketgroupid = NewBasketgroup( { name => $basket->{basketname},
205 booksellerid => $booksellerid,
206 deliveryplace => $branchcode,
207 billingplace => $branchcode,
210 ModBasket( { basketno => $basketno,
211 basketgroupid => $basketgroupid } );
212 print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
214 print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
219 confirm_close => "1",
220 booksellerid => $booksellerid,
221 booksellername => $bookseller->name,
222 basketno => $basket->{'basketno'},
223 basketname => $basket->{'basketname'},
224 basketgroupname => $basket->{'basketname'},
227 } elsif ($op eq 'reopen') {
228 ReopenBasket(scalar $query->param('basketno'));
229 print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
231 elsif ( $op eq 'ediorder' ) {
232 edi_close_and_order()
233 } elsif ( $op eq 'mod_users' ) {
234 my $basketusers_ids = $query->param('users_ids');
235 my @basketusers = split( /:/, $basketusers_ids );
236 ModBasketUsers($basketno, @basketusers);
237 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
239 } elsif ( $op eq 'mod_branch' ) {
240 my $branch = $query->param('branch');
241 $branch = undef if(defined $branch and $branch eq '');
243 basketno => $basket->{basketno},
246 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
250 if ( $op eq 'list' ) {
252 # get librarian branch...
253 if ( C4::Context->preference("IndependentBranches") ) {
254 my $userenv = C4::Context->userenv;
255 unless ( C4::Context->IsSuperLibrarian() ) {
256 my $validtest = ( $basket->{creationdate} eq '' )
257 || ( $userenv->{branch} eq $basket->{branch} )
258 || ( $userenv->{branch} eq '' )
259 || ( $basket->{branch} eq '' );
260 unless ($validtest) {
261 print $query->redirect("../mainpage.pl");
266 if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
267 push @branches_loop, {
268 branchcode => $userenv->{branch},
269 branchname => $userenv->{branchname},
275 my $branches = Koha::Libraries->search( {}, { order_by => ['branchname'] } )->unblessed;
276 foreach my $branch (@$branches) {
278 if (defined $basket->{branch}) {
279 $selected = 1 if $branch->{branchcode} eq $basket->{branch};
281 $selected = 1 if $branch->{branchcode} eq C4::Context->userenv->{branch};
283 push @branches_loop, {
284 branchcode => $branch->{branchcode},
285 branchname => $branch->{branchname},
286 selected => $selected
291 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
292 my ($basketgroup, $basketgroups);
293 my $patron = Koha::Patrons->find($loggedinuser);
294 if ($basket->{closedate} && haspermission($patron->userid, { acquisition => 'group_manage'} )) {
295 $basketgroups = GetBasketgroups($basket->{booksellerid});
296 for my $bg ( @{$basketgroups} ) {
297 if ($basket->{basketgroupid} && $basket->{basketgroupid} == $bg->{id}){
304 # if the basket is closed, calculate estimated delivery date
305 my $estimateddeliverydate;
306 if( $basket->{closedate} ) {
307 my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
308 ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->deliverytime);
309 $estimateddeliverydate = sprintf( "%04d-%02d-%02d", $year, $month, $day );
312 # if new basket, pre-fill infos
313 $basket->{creationdate} = "" unless ( $basket->{creationdate} );
314 $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
317 "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
318 $basket->{creationdate}, $basket->{authorisedby};
320 my @basketusers_ids = GetBasketUsers($basketno);
322 foreach my $basketuser_id (@basketusers_ids) {
323 # FIXME Could be improved with a search -in
324 my $basket_patron = Koha::Patrons->find( $basketuser_id );
325 push @basketusers, $basket_patron if $basket_patron;
328 my $active_currency = Koha::Acquisition::Currencies->get_active;
330 my @orders = GetOrders( $basketno );
335 my $total_quantity = 0;
336 my $total_tax_excluded = 0;
337 my $total_tax_included = 0;
338 my $total_tax_value = 0;
339 for my $order (@orders) {
340 my $line = get_order_infos( $order, $bookseller);
341 if ( $line->{uncertainprice} ) {
342 $template->param( uncertainprices => 1 );
345 $line->{tax_rate} = $line->{tax_rate_on_ordering} // 0;
346 $line->{tax_value} = $line->{tax_value_on_ordering} // 0;
348 push @books_loop, $line;
350 $foot{$$line{tax_rate}}{tax_rate} = $$line{tax_rate};
351 $foot{$$line{tax_rate}}{tax_value} += get_rounded_price($$line{tax_value});
352 $total_tax_value += $$line{tax_value};
353 $foot{$$line{tax_rate}}{quantity} += get_rounded_price($$line{quantity});
354 $total_quantity += $$line{quantity};
355 $foot{$$line{tax_rate}}{total_tax_excluded} += $$line{total_tax_excluded};
356 $total_tax_excluded += $$line{total_tax_excluded};
357 $foot{$$line{tax_rate}}{total_tax_included} += $$line{total_tax_included};
358 $total_tax_included += $$line{total_tax_included};
361 push @book_foot_loop, map {$_} values %foot;
363 # Get cancelled orders
364 my @cancelledorders = GetOrders($basketno, { cancelled => 1 });
365 my @cancelledorders_loop;
366 for my $order (@cancelledorders) {
367 my $line = get_order_infos( $order, $bookseller);
368 push @cancelledorders_loop, $line;
371 my $contract = GetContract({
372 contractnumber => $basket->{contractnumber}
375 if ($basket->{basketgroupid}){
376 $basketgroup = GetBasketgroup($basket->{basketgroupid});
378 my $budgets = GetBudgetHierarchy;
380 foreach my $r (@{$budgets}) {
381 next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
388 basketno => $basketno,
390 basketname => $basket->{'basketname'},
391 basketbranchcode => $basket->{branch},
392 basketnote => $basket->{note},
393 basketbooksellernote => $basket->{booksellernote},
394 basketcontractno => $basket->{contractnumber},
395 basketcontractname => $contract->{contractname},
396 branches_loop => \@branches_loop,
397 creationdate => $basket->{creationdate},
398 authorisedby => $basket->{authorisedby},
399 authorisedbyname => $basket->{authorisedbyname},
400 users_ids => join(':', @basketusers_ids),
401 users => \@basketusers,
402 closedate => $basket->{closedate},
403 estimateddeliverydate=> $estimateddeliverydate,
404 is_standing => $basket->{is_standing},
405 deliveryplace => $basket->{deliveryplace},
406 billingplace => $basket->{billingplace},
407 active => $bookseller->active,
408 booksellerid => $bookseller->id,
409 booksellername => $bookseller->name,
410 books_loop => \@books_loop,
411 book_foot_loop => \@book_foot_loop,
412 cancelledorders_loop => \@cancelledorders_loop,
413 total_quantity => $total_quantity,
414 total_tax_excluded => $total_tax_excluded,
415 total_tax_included => $total_tax_included,
416 total_tax_value => $total_tax_value,
417 currency => $active_currency->currency,
418 listincgst => $bookseller->listincgst,
419 basketgroups => $basketgroups,
420 basketgroup => $basketgroup,
421 grouped => $basket->{basketgroupid},
422 # The double negatives and booleans here mean:
423 # "A basket cannot be closed if there are no orders in it or it's a standing order basket."
425 # (The template has another implicit restriction that the order cannot be closed if there
426 # are any orders with uncertain prices.)
427 unclosable => @orders ? $basket->{is_standing} : 1,
428 has_budgets => $has_budgets,
429 duplinbatch => $duplinbatch,
430 csv_profiles => [ Koha::CsvProfiles->search({ type => 'sql', used_for => 'export_basket' }) ],
431 available_additional_fields => [ Koha::AdditionalFields->search( { tablename => 'aqbasket' } ) ],
432 additional_field_values => { map {
433 $_->field->name => $_->value
434 } Koha::Acquisition::Baskets->find($basketno)->additional_field_values->as_list },
438 $template->param( messages => \@messages );
439 output_html_with_http_headers $query, $cookie, $template->output;
441 sub get_order_infos {
443 my $bookseller = shift;
444 my $qty = $order->{'quantity'} || 0;
445 if ( !defined $order->{quantityreceived} ) {
446 $order->{quantityreceived} = 0;
448 my $budget = GetBudget($order->{budget_id});
449 my $basket = GetBasket($order->{basketno});
451 my %line = %{ $order };
452 # Don't show unreceived standing orders as received
453 $line{order_received} = ( $qty == $order->{'quantityreceived'} && ( $basket->{is_standing} ? $qty : 1 ) );
454 $line{basketno} = $basketno;
455 $line{budget_name} = $budget->{budget_name};
457 # If we have an actual cost that should be the total, otherwise use the ecost
458 my $cost_tax_included = $line{unitprice_tax_included} || $line{ecost_tax_included};
459 my $cost_tax_excluded = $line{unitprice_tax_excluded} || $line{ecost_tax_excluded};
460 $line{total_tax_included} = get_rounded_price($cost_tax_included) * $line{quantity};
461 $line{total_tax_excluded} = get_rounded_price($cost_tax_excluded) * $line{quantity};
462 $line{tax_value} = $line{tax_value_on_ordering};
463 $line{tax_rate} = $line{tax_rate_on_ordering};
465 if ( $line{'title'} ) {
466 my $volume = $order->{'volume'};
467 my $seriestitle = $order->{'seriestitle'};
468 $line{'title'} .= " / $seriestitle" if $seriestitle;
469 $line{'title'} .= " / $volume" if $volume;
472 my $biblionumber = $order->{'biblionumber'};
473 if ( $biblionumber ) { # The biblio still exists
474 my $biblio = Koha::Biblios->find( $biblionumber );
475 my $countbiblio = $biblio->active_orders->count;
477 my $ordernumber = $order->{'ordernumber'};
478 my $cnt_subscriptions = $biblio->subscriptions->count;
479 my $itemcount = $biblio->items->count;
480 my $holds_count = $biblio->holds->count;
481 my $order = Koha::Acquisition::Orders->find($ordernumber); # FIXME We should certainly do that at the beginning of this sub
482 my $items = $order->items;
483 my $itemholds = $biblio->holds->search({ itemnumber => { -in => [ $items->get_column('itemnumber') ] } })->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 == $items->count && !($cnt_subscriptions) && !($holds_count);
487 $line{items} = $itemcount - $items->count;
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};
497 $line{order_object} = $order;
501 my $suggestion = GetSuggestionInfoFromBiblionumber($line{biblionumber});
502 $line{suggestionid} = $$suggestion{suggestionid};
503 $line{surnamesuggestedby} = $$suggestion{surnamesuggestedby};
504 $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
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 CloseBasket($basketno);
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 );