Bug 14187: (QA followup) Properly check DB structure before altering it
[koha.git] / acqui / basket.pl
blob31b96bbaaa83e6ecb931a55d6eec16029557cfaf
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 Modern::Perl;
24 use C4::Auth;
25 use C4::Koha;
26 use C4::Output;
27 use CGI qw ( -utf8 );
28 use C4::Acquisition;
29 use C4::Budgets;
30 use C4::Contract;
31 use C4::Debug;
32 use C4::Biblio;
33 use C4::Members qw/GetMember/; #needed for permissions checking for changing basketgroup of a basket
34 use C4::Items;
35 use C4::Suggestions;
36 use Koha::Biblios;
37 use Koha::Acquisition::Booksellers;
38 use Koha::Libraries;
39 use C4::Letters qw/SendAlerts/;
40 use Date::Calc qw/Add_Delta_Days/;
41 use Koha::Database;
42 use Koha::EDI qw( create_edi_order get_edifact_ean );
44 =head1 NAME
46 basket.pl
48 =head1 DESCRIPTION
50 This script display all informations about basket for the supplier given
51 on input arg. Moreover, it allows us to add a new order for this supplier from
52 an existing record, a suggestion or a new record.
54 =head1 CGI PARAMETERS
56 =over 4
58 =item $basketno
60 The basket number.
62 =item booksellerid
64 the supplier this script have to display the basket.
66 =item order
68 =back
70 =cut
72 my $query = new CGI;
73 our $basketno = $query->param('basketno');
74 my $ean = $query->param('ean');
75 my $booksellerid = $query->param('booksellerid');
76 my $duplinbatch = $query->param('duplinbatch');
78 my ( $template, $loggedinuser, $cookie, $userflags ) = get_template_and_user(
80 template_name => "acqui/basket.tt",
81 query => $query,
82 type => "intranet",
83 authnotrequired => 0,
84 flagsrequired => { acquisition => 'order_manage' },
85 debug => 1,
89 my $basket = GetBasket($basketno);
90 $booksellerid = $basket->{booksellerid} unless $booksellerid;
91 my $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
92 my $schema = Koha::Database->new()->schema();
93 my $rs = $schema->resultset('VendorEdiAccount')->search(
94 { vendor_id => $booksellerid, } );
95 $template->param( ediaccount => ($rs->count > 0));
97 unless (CanUserManageBasket($loggedinuser, $basket, $userflags)) {
98 $template->param(
99 cannot_manage_basket => 1,
100 basketno => $basketno,
101 basketname => $basket->{basketname},
102 booksellerid => $booksellerid,
103 name => $bookseller->name,
105 output_html_with_http_headers $query, $cookie, $template->output;
106 exit;
109 # FIXME : what about the "discount" percentage?
110 # FIXME : the query->param('booksellerid') below is probably useless. The bookseller is always known from the basket
111 # if no booksellerid in parameter, get it from basket
112 # warn "=>".$basket->{booksellerid};
113 my $op = $query->param('op') // 'list';
115 my $confirm_pref= C4::Context->preference("BasketConfirmations") || '1';
116 $template->param( skip_confirm_reopen => 1) if $confirm_pref eq '2';
118 my @messages;
120 if ( $op eq 'delete_confirm' ) {
121 my $basketno = $query->param('basketno');
122 my $delbiblio = $query->param('delbiblio');
123 my @orders = GetOrders($basketno);
124 #Delete all orders included in that basket, and all items received.
125 foreach my $myorder (@orders){
126 DelOrder($myorder->{biblionumber},$myorder->{ordernumber});
128 # if $delbiblio = 1, delete the records if possible
129 if ((defined $delbiblio)and ($delbiblio ==1)){
130 my @cannotdelbiblios ;
131 foreach my $myorder (@orders){
132 my $biblionumber = $myorder->{'biblionumber'};
133 my $countbiblio = CountBiblioInOrders($biblionumber);
134 my $ordernumber = $myorder->{'ordernumber'};
135 my $subscriptions = scalar GetSubscriptionsId ($biblionumber);
136 my $itemcount = GetItemsCount($biblionumber);
137 my $error;
138 if ($countbiblio == 0 && $itemcount == 0 && $subscriptions == 0) {
139 $error = DelBiblio($myorder->{biblionumber}) }
140 else {
141 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
142 title=> $myorder->{'title'},
143 author=> $myorder->{'author'},
144 countbiblio=> $countbiblio,
145 itemcount=>$itemcount,
146 subscriptions=>$subscriptions};
148 if ($error) {
149 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
150 title=> $myorder->{'title'},
151 author=> $myorder->{'author'},
152 othererror=> $error};
155 $template->param( cannotdelbiblios => \@cannotdelbiblios );
157 # delete the basket
158 DelBasket($basketno,);
159 $template->param( delete_confirmed => 1 );
160 } elsif ( !$bookseller ) {
161 $template->param( NO_BOOKSELLER => 1 );
162 } elsif ($op eq 'export') {
163 print $query->header(
164 -type => 'text/csv',
165 -attachment => 'basket' . $basket->{'basketno'} . '.csv',
167 print GetBasketAsCSV($query->param('basketno'), $query);
168 exit;
169 } elsif ($op eq 'email') {
170 my $err = eval {
171 SendAlerts( 'orderacquisition', $query->param('basketno'), 'ACQORDER' );
173 if ( $@ ) {
174 push @messages, { type => 'error', code => $@ };
175 } elsif ( ref $err and exists $err->{error} ) {
176 push @messages, { type => 'error', code => $err->{error} };
177 } else {
178 push @messages, { type => 'message', code => 'email_sent' };
181 $op = 'list';
182 } elsif ($op eq 'close') {
183 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
184 if ($confirm) {
185 my $basketno = $query->param('basketno');
186 my $booksellerid = $query->param('booksellerid');
187 $basketno =~ /^\d+$/ and CloseBasket($basketno);
188 # if requested, create basket group, close it and attach the basket
189 if ($query->param('createbasketgroup')) {
190 my $branchcode;
191 if(C4::Context->userenv and C4::Context->userenv->{'branch'}
192 and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET") {
193 $branchcode = C4::Context->userenv->{'branch'};
195 my $basketgroupid = NewBasketgroup( { name => $basket->{basketname},
196 booksellerid => $booksellerid,
197 deliveryplace => $branchcode,
198 billingplace => $branchcode,
199 closed => 1,
201 ModBasket( { basketno => $basketno,
202 basketgroupid => $basketgroupid } );
203 print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
204 } else {
205 print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
207 exit;
208 } else {
209 $template->param(
210 confirm_close => "1",
211 booksellerid => $booksellerid,
212 basketno => $basket->{'basketno'},
213 basketname => $basket->{'basketname'},
214 basketgroupname => $basket->{'basketname'},
217 } elsif ($op eq 'reopen') {
218 ReopenBasket($query->param('basketno'));
219 print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
221 elsif ( $op eq 'ediorder' ) {
222 edi_close_and_order()
223 } elsif ( $op eq 'mod_users' ) {
224 my $basketusers_ids = $query->param('users_ids');
225 my @basketusers = split( /:/, $basketusers_ids );
226 ModBasketUsers($basketno, @basketusers);
227 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
228 exit;
229 } elsif ( $op eq 'mod_branch' ) {
230 my $branch = $query->param('branch');
231 $branch = undef if(defined $branch and $branch eq '');
232 ModBasket({
233 basketno => $basket->{basketno},
234 branch => $branch
236 print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
237 exit;
240 if ( $op eq 'list' ) {
241 my @branches_loop;
242 # get librarian branch...
243 if ( C4::Context->preference("IndependentBranches") ) {
244 my $userenv = C4::Context->userenv;
245 unless ( C4::Context->IsSuperLibrarian() ) {
246 my $validtest = ( $basket->{creationdate} eq '' )
247 || ( $userenv->{branch} eq $basket->{branch} )
248 || ( $userenv->{branch} eq '' )
249 || ( $basket->{branch} eq '' );
250 unless ($validtest) {
251 print $query->redirect("../mainpage.pl");
252 exit 0;
256 if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
257 push @branches_loop, {
258 branchcode => $userenv->{branch},
259 branchname => $userenv->{branchname},
260 selected => 1,
263 } else {
264 # get branches
265 my $branches = Koha::Libraries->search( {}, { order_by => ['branchname'] } )->unblessed;
266 foreach my $branch (@$branches) {
267 my $selected = 0;
268 if (defined $basket->{branch}) {
269 $selected = 1 if $branch->{branchcode} eq $basket->{branch};
270 } else {
271 $selected = 1 if $branch->{branchcode} eq C4::Context->userenv->{branch};
273 push @branches_loop, {
274 branchcode => $branch->{branchcode},
275 branchname => $branch->{branchname},
276 selected => $selected
281 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
282 my ($basketgroup, $basketgroups);
283 my $staffuser = GetMember(borrowernumber => $loggedinuser);
284 if ($basket->{closedate} && haspermission($staffuser->{userid}, { acquisition => 'group_manage'} )) {
285 $basketgroups = GetBasketgroups($basket->{booksellerid});
286 for my $bg ( @{$basketgroups} ) {
287 if ($basket->{basketgroupid} && $basket->{basketgroupid} == $bg->{id}){
288 $bg->{default} = 1;
289 $basketgroup = $bg;
294 # if the basket is closed, calculate estimated delivery date
295 my $estimateddeliverydate;
296 if( $basket->{closedate} ) {
297 my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
298 ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->deliverytime);
299 $estimateddeliverydate = sprintf( "%04d-%02d-%02d", $year, $month, $day );
302 # if new basket, pre-fill infos
303 $basket->{creationdate} = "" unless ( $basket->{creationdate} );
304 $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
305 $debug
306 and warn sprintf
307 "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
308 $basket->{creationdate}, $basket->{authorisedby};
310 my @basketusers_ids = GetBasketUsers($basketno);
311 my @basketusers;
312 foreach my $basketuser_id (@basketusers_ids) {
313 my $basketuser = GetMember(borrowernumber => $basketuser_id);
314 push @basketusers, $basketuser if $basketuser;
317 my $active_currency = Koha::Acquisition::Currencies->get_active;
319 my @orders = GetOrders( $basketno );
320 my @books_loop;
322 my @book_foot_loop;
323 my %foot;
324 my $total_quantity = 0;
325 my $total_tax_excluded = 0;
326 my $total_tax_included = 0;
327 my $total_tax_value = 0;
328 for my $order (@orders) {
329 my $line = get_order_infos( $order, $bookseller);
330 if ( $line->{uncertainprice} ) {
331 $template->param( uncertainprices => 1 );
334 $line->{tax_rate} = $line->{tax_rate_on_ordering};
335 $line->{tax_value} = $line->{tax_value_on_ordering};
337 push @books_loop, $line;
339 $foot{$$line{tax_rate}}{tax_rate} = $$line{tax_rate};
340 $foot{$$line{tax_rate}}{tax_value} += $$line{tax_value};
341 $total_tax_value += $$line{tax_value};
342 $foot{$$line{tax_rate}}{quantity} += $$line{quantity};
343 $total_quantity += $$line{quantity};
344 $foot{$$line{tax_rate}}{total_tax_excluded} += $$line{total_tax_excluded};
345 $total_tax_excluded += $$line{total_tax_excluded};
346 $foot{$$line{tax_rate}}{total_tax_included} += $$line{total_tax_included};
347 $total_tax_included += $$line{total_tax_included};
350 push @book_foot_loop, map {$_} values %foot;
352 # Get cancelled orders
353 my @cancelledorders = GetOrders($basketno, { cancelled => 1 });
354 my @cancelledorders_loop;
355 for my $order (@cancelledorders) {
356 my $line = get_order_infos( $order, $bookseller);
357 push @cancelledorders_loop, $line;
360 my $contract = GetContract({
361 contractnumber => $basket->{contractnumber}
364 if ($basket->{basketgroupid}){
365 $basketgroup = GetBasketgroup($basket->{basketgroupid});
367 my $borrower= GetMember('borrowernumber' => $loggedinuser);
368 my $budgets = GetBudgetHierarchy;
369 my $has_budgets = 0;
370 foreach my $r (@{$budgets}) {
371 if (!defined $r->{budget_amount} || $r->{budget_amount} == 0) {
372 next;
374 next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
376 $has_budgets = 1;
377 last;
380 $template->param(
381 basketno => $basketno,
382 basket => $basket,
383 basketname => $basket->{'basketname'},
384 basketbranchcode => $basket->{branch},
385 basketnote => $basket->{note},
386 basketbooksellernote => $basket->{booksellernote},
387 basketcontractno => $basket->{contractnumber},
388 basketcontractname => $contract->{contractname},
389 branches_loop => \@branches_loop,
390 creationdate => $basket->{creationdate},
391 authorisedby => $basket->{authorisedby},
392 authorisedbyname => $basket->{authorisedbyname},
393 users_ids => join(':', @basketusers_ids),
394 users => \@basketusers,
395 closedate => $basket->{closedate},
396 estimateddeliverydate=> $estimateddeliverydate,
397 is_standing => $basket->{is_standing},
398 deliveryplace => $basket->{deliveryplace},
399 billingplace => $basket->{billingplace},
400 active => $bookseller->active,
401 booksellerid => $bookseller->id,
402 name => $bookseller->name,
403 books_loop => \@books_loop,
404 book_foot_loop => \@book_foot_loop,
405 cancelledorders_loop => \@cancelledorders_loop,
406 total_quantity => $total_quantity,
407 total_tax_excluded => $total_tax_excluded,
408 total_tax_included => $total_tax_included,
409 total_tax_value => $total_tax_value,
410 currency => $active_currency->currency,
411 listincgst => $bookseller->listincgst,
412 basketgroups => $basketgroups,
413 basketgroup => $basketgroup,
414 grouped => $basket->{basketgroupid},
415 # The double negatives and booleans here mean:
416 # "A basket cannot be closed if there are no orders in it or it's a standing order basket."
418 # (The template has another implicit restriction that the order cannot be closed if there
419 # are any orders with uncertain prices.)
420 unclosable => @orders ? $basket->{is_standing} : 1,
421 has_budgets => $has_budgets,
422 duplinbatch => $duplinbatch,
426 $template->param( messages => \@messages );
427 output_html_with_http_headers $query, $cookie, $template->output;
429 sub get_order_infos {
430 my $order = shift;
431 my $bookseller = shift;
432 my $qty = $order->{'quantity'} || 0;
433 if ( !defined $order->{quantityreceived} ) {
434 $order->{quantityreceived} = 0;
436 my $budget = GetBudget($order->{budget_id});
437 my $basket = GetBasket($order->{basketno});
439 my %line = %{ $order };
440 # Don't show unreceived standing orders as received
441 $line{order_received} = ( $qty == $order->{'quantityreceived'} && ( $basket->{is_standing} ? $qty : 1 ) );
442 $line{basketno} = $basketno;
443 $line{budget_name} = $budget->{budget_name};
445 $line{total_tax_included} = $line{ecost_tax_included} * $line{quantity};
446 $line{total_tax_excluded} = $line{ecost_tax_excluded} * $line{quantity};
447 $line{tax_value} = $line{tax_value_on_ordering};
448 $line{tax_rate} = $line{tax_rate_on_ordering};
450 if ( $line{uncertainprice} ) {
451 $line{rrp_tax_excluded} .= ' (Uncertain)';
453 if ( $line{'title'} ) {
454 my $volume = $order->{'volume'};
455 my $seriestitle = $order->{'seriestitle'};
456 $line{'title'} .= " / $seriestitle" if $seriestitle;
457 $line{'title'} .= " / $volume" if $volume;
460 my $biblionumber = $order->{'biblionumber'};
461 my $biblio = Koha::Biblios->find( $biblionumber );
462 my $countbiblio = CountBiblioInOrders($biblionumber);
463 my $ordernumber = $order->{'ordernumber'};
464 my @subscriptions = GetSubscriptionsId ($biblionumber);
465 my $itemcount = GetItemsCount($biblionumber);
466 my $holds_count = $biblio->holds->count;
467 my @items = GetItemnumbersFromOrder( $ordernumber );
468 my $itemholds;
469 foreach my $item (@items){
470 my $nb = GetItemHolds($biblionumber, $item);
471 if ($nb){
472 $itemholds += $nb;
475 # 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
476 $line{can_del_bib} = 1 if $countbiblio <= 1 && $itemcount == scalar @items && !(@subscriptions) && !($holds_count);
477 $line{items} = ($itemcount) - (scalar @items);
478 $line{left_item} = 1 if $line{items} >= 1;
479 $line{left_biblio} = 1 if $countbiblio > 1;
480 $line{biblios} = $countbiblio - 1;
481 $line{left_subscription} = 1 if scalar @subscriptions >= 1;
482 $line{subscriptions} = scalar @subscriptions;
483 ($holds_count >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
484 $line{left_holds_on_order} = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
485 $line{holds} = $holds_count;
486 $line{holds_on_order} = $itemholds?$itemholds:$holds_count if $line{left_holds_on_order};
489 my $suggestion = GetSuggestionInfoFromBiblionumber($line{biblionumber});
490 $line{suggestionid} = $$suggestion{suggestionid};
491 $line{surnamesuggestedby} = $$suggestion{surnamesuggestedby};
492 $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
494 foreach my $key (qw(transferred_from transferred_to)) {
495 if ($line{$key}) {
496 my $order = GetOrder($line{$key});
497 my $bookseller = Koha::Acquisition::Booksellers->find( $basket->{booksellerid} );
498 $line{$key} = {
499 order => $order,
500 basket => $basket,
501 bookseller => $bookseller,
502 timestamp => $line{$key . '_timestamp'},
507 return \%line;
510 sub edi_close_and_order {
511 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
512 if ($confirm) {
513 my $edi_params = {
514 basketno => $basketno,
515 ean => $ean,
517 if ( $basket->{branch} ) {
518 $edi_params->{branchcode} = $basket->{branch};
520 if ( create_edi_order($edi_params) ) {
521 #$template->param( edifile => 1 );
523 CloseBasket($basketno);
525 # if requested, create basket group, close it and attach the basket
526 if ( $query->param('createbasketgroup') ) {
527 my $branchcode;
528 if ( C4::Context->userenv
529 and C4::Context->userenv->{'branch'}
530 and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET" )
532 $branchcode = C4::Context->userenv->{'branch'};
534 my $basketgroupid = NewBasketgroup(
536 name => $basket->{basketname},
537 booksellerid => $booksellerid,
538 deliveryplace => $branchcode,
539 billingplace => $branchcode,
540 closed => 1,
543 ModBasket(
545 basketno => $basketno,
546 basketgroupid => $basketgroupid
549 print $query->redirect(
550 "/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=$booksellerid&closed=1"
553 else {
554 print $query->redirect(
555 "/cgi-bin/koha/acqui/booksellers.pl?booksellerid=$booksellerid"
558 exit;
560 else {
561 $template->param(
562 edi_confirm => 1,
563 booksellerid => $booksellerid,
564 basketno => $basket->{basketno},
565 basketname => $basket->{basketname},
566 basketgroupname => $basket->{basketname},
568 if ($ean) {
569 $template->param( ean => $ean );
573 return;