Bug 15531: (followup) Use a quantity of 1, not null, for standing orders
[koha.git] / acqui / basket.pl
blobd1f37e6c34c2f837be755c9a5900afe254f6315e
1 #!/usr/bin/perl
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>.
23 use strict;
24 use warnings;
25 use C4::Auth;
26 use C4::Koha;
27 use C4::Output;
28 use CGI qw ( -utf8 );
29 use C4::Acquisition;
30 use C4::Budgets;
31 use C4::Branch;
32 use C4::Contract;
33 use C4::Debug;
34 use C4::Biblio;
35 use C4::Members qw/GetMember/; #needed for permissions checking for changing basketgroup of a basket
36 use C4::Items;
37 use C4::Suggestions;
38 use Date::Calc qw/Add_Delta_Days/;
39 use Koha::Database;
40 use Koha::EDI qw( create_edi_order get_edifact_ean );
42 =head1 NAME
44 basket.pl
46 =head1 DESCRIPTION
48 This script display all informations about basket for the supplier given
49 on input arg. Moreover, it allows us to add a new order for this supplier from
50 an existing record, a suggestion or a new record.
52 =head1 CGI PARAMETERS
54 =over 4
56 =item $basketno
58 The basket number.
60 =item booksellerid
62 the supplier this script have to display the basket.
64 =item order
66 =back
68 =cut
70 my $query = new CGI;
71 our $basketno = $query->param('basketno');
72 my $ean = $query->param('ean');
73 my $booksellerid = $query->param('booksellerid');
74 my $duplinbatch = $query->param('duplinbatch');
76 my ( $template, $loggedinuser, $cookie, $userflags ) = get_template_and_user(
78 template_name => "acqui/basket.tt",
79 query => $query,
80 type => "intranet",
81 authnotrequired => 0,
82 flagsrequired => { acquisition => 'order_manage' },
83 debug => 1,
87 my $basket = GetBasket($basketno);
88 $booksellerid = $basket->{booksellerid} unless $booksellerid;
89 my $bookseller = Koha::Acquisition::Bookseller->fetch({ id => $booksellerid });
90 my $schema = Koha::Database->new()->schema();
91 my $rs = $schema->resultset('VendorEdiAccount')->search(
92 { vendor_id => $booksellerid, } );
93 $template->param( ediaccount => ($rs->count > 0));
95 unless (CanUserManageBasket($loggedinuser, $basket, $userflags)) {
96 $template->param(
97 cannot_manage_basket => 1,
98 basketno => $basketno,
99 basketname => $basket->{basketname},
100 booksellerid => $booksellerid,
101 name => $bookseller->{name}
103 output_html_with_http_headers $query, $cookie, $template->output;
104 exit;
107 # FIXME : what about the "discount" percentage?
108 # FIXME : the query->param('booksellerid') below is probably useless. The bookseller is always known from the basket
109 # if no booksellerid in parameter, get it from basket
110 # warn "=>".$basket->{booksellerid};
111 my $op = $query->param('op');
112 if (!defined $op) {
113 $op = q{};
116 my $confirm_pref= C4::Context->preference("BasketConfirmations") || '1';
117 $template->param( skip_confirm_reopen => 1) if $confirm_pref eq '2';
119 if ( $op eq 'delete_confirm' ) {
120 my $basketno = $query->param('basketno');
121 my $delbiblio = $query->param('delbiblio');
122 my @orders = GetOrders($basketno);
123 #Delete all orders included in that basket, and all items received.
124 foreach my $myorder (@orders){
125 DelOrder($myorder->{biblionumber},$myorder->{ordernumber});
127 # if $delbiblio = 1, delete the records if possible
128 if ((defined $delbiblio)and ($delbiblio ==1)){
129 my @cannotdelbiblios ;
130 foreach my $myorder (@orders){
131 my $biblionumber = $myorder->{'biblionumber'};
132 my $countbiblio = CountBiblioInOrders($biblionumber);
133 my $ordernumber = $myorder->{'ordernumber'};
134 my $subscriptions = scalar GetSubscriptionsId ($biblionumber);
135 my $itemcount = GetItemsCount($biblionumber);
136 my $error;
137 if ($countbiblio == 0 && $itemcount == 0 && $subscriptions == 0) {
138 $error = DelBiblio($myorder->{biblionumber}) }
139 else {
140 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
141 title=> $myorder->{'title'},
142 author=> $myorder->{'author'},
143 countbiblio=> $countbiblio,
144 itemcount=>$itemcount,
145 subscriptions=>$subscriptions};
147 if ($error) {
148 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
149 title=> $myorder->{'title'},
150 author=> $myorder->{'author'},
151 othererror=> $error};
154 $template->param( cannotdelbiblios => \@cannotdelbiblios );
156 # delete the basket
157 DelBasket($basketno,);
158 $template->param( delete_confirmed => 1 );
159 } elsif ( !$bookseller ) {
160 $template->param( NO_BOOKSELLER => 1 );
161 } elsif ($op eq 'export') {
162 print $query->header(
163 -type => 'text/csv',
164 -attachment => 'basket' . $basket->{'basketno'} . '.csv',
166 print GetBasketAsCSV($query->param('basketno'), $query);
167 exit;
168 } elsif ($op eq 'close') {
169 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
170 if ($confirm) {
171 my $basketno = $query->param('basketno');
172 my $booksellerid = $query->param('booksellerid');
173 $basketno =~ /^\d+$/ and CloseBasket($basketno);
174 # if requested, create basket group, close it and attach the basket
175 if ($query->param('createbasketgroup')) {
176 my $branchcode;
177 if(C4::Context->userenv and C4::Context->userenv->{'branch'}
178 and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET") {
179 $branchcode = C4::Context->userenv->{'branch'};
181 my $basketgroupid = NewBasketgroup( { name => $basket->{basketname},
182 booksellerid => $booksellerid,
183 deliveryplace => $branchcode,
184 billingplace => $branchcode,
185 closed => 1,
187 ModBasket( { basketno => $basketno,
188 basketgroupid => $basketgroupid } );
189 print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
190 } else {
191 print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
193 exit;
194 } else {
195 $template->param(
196 confirm_close => "1",
197 booksellerid => $booksellerid,
198 basketno => $basket->{'basketno'},
199 basketname => $basket->{'basketname'},
200 basketgroupname => $basket->{'basketname'},
203 } elsif ($op eq 'reopen') {
204 ReopenBasket($query->param('basketno'));
205 print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
207 elsif ( $op eq 'ediorder' ) {
208 edi_close_and_order()
209 } elsif ( $op eq 'mod_users' ) {
210 my $basketusers_ids = $query->param('users_ids');
211 my @basketusers = split( /:/, $basketusers_ids );
212 ModBasketUsers($basketno, @basketusers);
213 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
214 exit;
215 } elsif ( $op eq 'mod_branch' ) {
216 my $branch = $query->param('branch');
217 $branch = undef if(defined $branch and $branch eq '');
218 ModBasket({
219 basketno => $basket->{basketno},
220 branch => $branch
222 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
223 exit;
224 } else {
225 my @branches_loop;
226 # get librarian branch...
227 if ( C4::Context->preference("IndependentBranches") ) {
228 my $userenv = C4::Context->userenv;
229 unless ( C4::Context->IsSuperLibrarian() ) {
230 my $validtest = ( $basket->{creationdate} eq '' )
231 || ( $userenv->{branch} eq $basket->{branch} )
232 || ( $userenv->{branch} eq '' )
233 || ( $basket->{branch} eq '' );
234 unless ($validtest) {
235 print $query->redirect("../mainpage.pl");
236 exit 1;
239 if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
240 push @branches_loop, {
241 branchcode => $userenv->{branch},
242 branchname => $userenv->{branchname},
243 selected => 1,
246 } else {
247 # get branches
248 my $branches = C4::Branch::GetBranches;
249 my @branchcodes = sort {
250 $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname}
251 } keys %$branches;
252 foreach my $branch (@branchcodes) {
253 my $selected = 0;
254 if (defined $basket->{branch}) {
255 $selected = 1 if $branch eq $basket->{branch};
256 } else {
257 $selected = 1 if $branch eq C4::Context->userenv->{branch};
259 push @branches_loop, {
260 branchcode => $branch,
261 branchname => $branches->{$branch}->{branchname},
262 selected => $selected
267 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
268 my ($basketgroup, $basketgroups);
269 my $staffuser = GetMember(borrowernumber => $loggedinuser);
270 if ($basket->{closedate} && haspermission($staffuser->{userid}, { acquisition => 'group_manage'} )) {
271 $basketgroups = GetBasketgroups($basket->{booksellerid});
272 for my $bg ( @{$basketgroups} ) {
273 if ($basket->{basketgroupid} && $basket->{basketgroupid} == $bg->{id}){
274 $bg->{default} = 1;
275 $basketgroup = $bg;
280 # if the basket is closed, calculate estimated delivery date
281 my $estimateddeliverydate;
282 if( $basket->{closedate} ) {
283 my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
284 ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->{deliverytime});
285 $estimateddeliverydate = sprintf( "%04d-%02d-%02d", $year, $month, $day );
288 # if new basket, pre-fill infos
289 $basket->{creationdate} = "" unless ( $basket->{creationdate} );
290 $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
291 $debug
292 and warn sprintf
293 "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
294 $basket->{creationdate}, $basket->{authorisedby};
296 my @basketusers_ids = GetBasketUsers($basketno);
297 my @basketusers;
298 foreach my $basketuser_id (@basketusers_ids) {
299 my $basketuser = GetMember(borrowernumber => $basketuser_id);
300 push @basketusers, $basketuser if $basketuser;
303 my $active_currency = Koha::Acquisition::Currencies->get_active;
305 my @orders = GetOrders( $basketno );
306 my @books_loop;
308 my @book_foot_loop;
309 my %foot;
310 my $total_quantity = 0;
311 my $total_gste = 0;
312 my $total_gsti = 0;
313 my $total_gstvalue = 0;
314 for my $order (@orders) {
315 $order = C4::Acquisition::populate_order_with_prices({ order => $order, booksellerid => $booksellerid, ordering => 1 });
316 my $line = get_order_infos( $order, $bookseller);
317 if ( $line->{uncertainprice} ) {
318 $template->param( uncertainprices => 1 );
321 push @books_loop, $line;
323 $foot{$$line{gstrate}}{gstrate} = $$line{gstrate};
324 $foot{$$line{gstrate}}{gstvalue} += $$line{gstvalue};
325 $total_gstvalue += $$line{gstvalue};
326 $foot{$$line{gstrate}}{quantity} += $$line{quantity};
327 $total_quantity += $$line{quantity};
328 $foot{$$line{gstrate}}{totalgste} += $$line{totalgste};
329 $total_gste += $$line{totalgste};
330 $foot{$$line{gstrate}}{totalgsti} += $$line{totalgsti};
331 $total_gsti += $$line{totalgsti};
334 push @book_foot_loop, map {$_} values %foot;
336 # Get cancelled orders
337 my @cancelledorders = GetOrders($basketno, { cancelled => 1 });
338 my @cancelledorders_loop;
339 for my $order (@cancelledorders) {
340 $order = C4::Acquisition::populate_order_with_prices({ order => $order, booksellerid => $booksellerid, ordering => 1 });
341 my $line = get_order_infos( $order, $bookseller);
342 push @cancelledorders_loop, $line;
345 my $contract = GetContract({
346 contractnumber => $basket->{contractnumber}
349 if ($basket->{basketgroupid}){
350 $basketgroup = GetBasketgroup($basket->{basketgroupid});
351 $basketgroup->{deliveryplacename} = C4::Branch::GetBranchName( $basketgroup->{deliveryplace} );
352 $basketgroup->{billingplacename} = C4::Branch::GetBranchName( $basketgroup->{billingplace} );
354 my $borrower= GetMember('borrowernumber' => $loggedinuser);
355 my $budgets = GetBudgetHierarchy;
356 my $has_budgets = 0;
357 foreach my $r (@{$budgets}) {
358 if (!defined $r->{budget_amount} || $r->{budget_amount} == 0) {
359 next;
361 next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
363 $has_budgets = 1;
364 last;
367 $template->param(
368 basketno => $basketno,
369 basket => $basket,
370 basketname => $basket->{'basketname'},
371 basketbranchname => C4::Branch::GetBranchName($basket->{branch}),
372 basketnote => $basket->{note},
373 basketbooksellernote => $basket->{booksellernote},
374 basketcontractno => $basket->{contractnumber},
375 basketcontractname => $contract->{contractname},
376 branches_loop => \@branches_loop,
377 creationdate => $basket->{creationdate},
378 authorisedby => $basket->{authorisedby},
379 authorisedbyname => $basket->{authorisedbyname},
380 users_ids => join(':', @basketusers_ids),
381 users => \@basketusers,
382 closedate => $basket->{closedate},
383 estimateddeliverydate=> $estimateddeliverydate,
384 is_standing => $basket->{is_standing},
385 deliveryplace => C4::Branch::GetBranchName( $basket->{deliveryplace} ),
386 billingplace => C4::Branch::GetBranchName( $basket->{billingplace} ),
387 active => $bookseller->{'active'},
388 booksellerid => $bookseller->{'id'},
389 name => $bookseller->{'name'},
390 books_loop => \@books_loop,
391 book_foot_loop => \@book_foot_loop,
392 cancelledorders_loop => \@cancelledorders_loop,
393 total_quantity => $total_quantity,
394 total_gste => sprintf( "%.2f", $total_gste ),
395 total_gsti => sprintf( "%.2f", $total_gsti ),
396 total_gstvalue => sprintf( "%.2f", $total_gstvalue ),
397 currency => $active_currency->currency,
398 listincgst => $bookseller->{listincgst},
399 basketgroups => $basketgroups,
400 basketgroup => $basketgroup,
401 grouped => $basket->{basketgroupid},
402 # The double negatives and booleans here mean:
403 # "A basket cannot be closed if there are no orders in it or it's a standing order basket."
405 # (The template has another implicit restriction that the order cannot be closed if there
406 # are any orders with uncertain prices.)
407 unclosable => @orders ? $basket->{is_standing} : 1,
408 has_budgets => $has_budgets,
409 duplinbatch => $duplinbatch,
413 sub get_order_infos {
414 my $order = shift;
415 my $bookseller = shift;
416 my $qty = $order->{'quantity'} || 0;
417 if ( !defined $order->{quantityreceived} ) {
418 $order->{quantityreceived} = 0;
420 my $budget = GetBudget($order->{budget_id});
421 my $basket = GetBasket($order->{basketno});
423 my %line = %{ $order };
424 # Don't show unreceived standing orders as received
425 $line{order_received} = ( $qty == $order->{'quantityreceived'} && ( $basket->{is_standing} ? $qty : 1 ) );
426 $line{basketno} = $basketno;
427 $line{budget_name} = $budget->{budget_name};
429 if ( $line{uncertainprice} ) {
430 $line{rrpgste} .= ' (Uncertain)';
432 if ( $line{'title'} ) {
433 my $volume = $order->{'volume'};
434 my $seriestitle = $order->{'seriestitle'};
435 $line{'title'} .= " / $seriestitle" if $seriestitle;
436 $line{'title'} .= " / $volume" if $volume;
439 my $biblionumber = $order->{'biblionumber'};
440 my $countbiblio = CountBiblioInOrders($biblionumber);
441 my $ordernumber = $order->{'ordernumber'};
442 my @subscriptions = GetSubscriptionsId ($biblionumber);
443 my $itemcount = GetItemsCount($biblionumber);
444 my $holds = GetHolds ($biblionumber);
445 my @items = GetItemnumbersFromOrder( $ordernumber );
446 my $itemholds;
447 foreach my $item (@items){
448 my $nb = GetItemHolds($biblionumber, $item);
449 if ($nb){
450 $itemholds += $nb;
453 # 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
454 $line{can_del_bib} = 1 if $countbiblio <= 1 && $itemcount == scalar @items && !(@subscriptions) && !($holds);
455 $line{items} = ($itemcount) - (scalar @items);
456 $line{left_item} = 1 if $line{items} >= 1;
457 $line{left_biblio} = 1 if $countbiblio > 1;
458 $line{biblios} = $countbiblio - 1;
459 $line{left_subscription} = 1 if scalar @subscriptions >= 1;
460 $line{subscriptions} = scalar @subscriptions;
461 ($holds >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
462 $line{left_holds_on_order} = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
463 $line{holds} = $holds;
464 $line{holds_on_order} = $itemholds?$itemholds:$holds if $line{left_holds_on_order};
467 my $suggestion = GetSuggestionInfoFromBiblionumber($line{biblionumber});
468 $line{suggestionid} = $$suggestion{suggestionid};
469 $line{surnamesuggestedby} = $$suggestion{surnamesuggestedby};
470 $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
472 foreach my $key (qw(transferred_from transferred_to)) {
473 if ($line{$key}) {
474 my $order = GetOrder($line{$key});
475 my $bookseller = Koha::Acquisition::Bookseller->fetch({ id => $basket->{booksellerid} });
476 $line{$key} = {
477 order => $order,
478 basket => $basket,
479 bookseller => $bookseller,
480 timestamp => $line{$key . '_timestamp'},
485 return \%line;
488 output_html_with_http_headers $query, $cookie, $template->output;
491 sub edi_close_and_order {
492 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
493 if ($confirm) {
494 my $edi_params = {
495 basketno => $basketno,
496 ean => $ean,
498 if ( $basket->{branch} ) {
499 $edi_params->{branchcode} = $basket->{branch};
501 if ( create_edi_order($edi_params) ) {
502 #$template->param( edifile => 1 );
504 CloseBasket($basketno);
506 # if requested, create basket group, close it and attach the basket
507 if ( $query->param('createbasketgroup') ) {
508 my $branchcode;
509 if ( C4::Context->userenv
510 and C4::Context->userenv->{'branch'}
511 and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET" )
513 $branchcode = C4::Context->userenv->{'branch'};
515 my $basketgroupid = NewBasketgroup(
517 name => $basket->{basketname},
518 booksellerid => $booksellerid,
519 deliveryplace => $branchcode,
520 billingplace => $branchcode,
521 closed => 1,
524 ModBasket(
526 basketno => $basketno,
527 basketgroupid => $basketgroupid
530 print $query->redirect(
531 "/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=$booksellerid&closed=1"
534 else {
535 print $query->redirect(
536 "/cgi-bin/koha/acqui/booksellers.pl?booksellerid=$booksellerid"
539 exit;
541 else {
542 $template->param(
543 edi_confirm => 1,
544 booksellerid => $booksellerid,
545 basketno => $basket->{basketno},
546 basketname => $basket->{basketname},
547 basketgroupname => $basket->{basketname},
549 if ($ean) {
550 $template->param( ean => $ean );
554 return;