Bug 17477: Duplicating a subfield yields an empty subfield tag
[koha.git] / acqui / basket.pl
blobf3bf36aaaa2e60792bfb66ba8415969082189e8a
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::Contract;
32 use C4::Debug;
33 use C4::Biblio;
34 use C4::Members qw/GetMember/; #needed for permissions checking for changing basketgroup of a basket
35 use C4::Items;
36 use C4::Suggestions;
37 use Koha::Libraries;
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;
240 if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
241 push @branches_loop, {
242 branchcode => $userenv->{branch},
243 branchname => $userenv->{branchname},
244 selected => 1,
247 } else {
248 # get branches
249 my $branches = Koha::Libraries->search( {}, { order_by => ['branchname'] } )->unblessed;
250 foreach my $branch (@$branches) {
251 my $selected = 0;
252 if (defined $basket->{branch}) {
253 $selected = 1 if $branch->{branchcode} eq $basket->{branch};
254 } else {
255 $selected = 1 if $branch->{branchcode} eq C4::Context->userenv->{branch};
257 push @branches_loop, {
258 branchcode => $branch->{branchcode},
259 branchname => $branch->{branchname},
260 selected => $selected
265 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
266 my ($basketgroup, $basketgroups);
267 my $staffuser = GetMember(borrowernumber => $loggedinuser);
268 if ($basket->{closedate} && haspermission($staffuser->{userid}, { acquisition => 'group_manage'} )) {
269 $basketgroups = GetBasketgroups($basket->{booksellerid});
270 for my $bg ( @{$basketgroups} ) {
271 if ($basket->{basketgroupid} && $basket->{basketgroupid} == $bg->{id}){
272 $bg->{default} = 1;
273 $basketgroup = $bg;
278 # if the basket is closed, calculate estimated delivery date
279 my $estimateddeliverydate;
280 if( $basket->{closedate} ) {
281 my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
282 ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->{deliverytime});
283 $estimateddeliverydate = sprintf( "%04d-%02d-%02d", $year, $month, $day );
286 # if new basket, pre-fill infos
287 $basket->{creationdate} = "" unless ( $basket->{creationdate} );
288 $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
289 $debug
290 and warn sprintf
291 "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
292 $basket->{creationdate}, $basket->{authorisedby};
294 my @basketusers_ids = GetBasketUsers($basketno);
295 my @basketusers;
296 foreach my $basketuser_id (@basketusers_ids) {
297 my $basketuser = GetMember(borrowernumber => $basketuser_id);
298 push @basketusers, $basketuser if $basketuser;
301 my $active_currency = Koha::Acquisition::Currencies->get_active;
303 my @orders = GetOrders( $basketno );
304 my @books_loop;
306 my @book_foot_loop;
307 my %foot;
308 my $total_quantity = 0;
309 my $total_gste = 0;
310 my $total_gsti = 0;
311 my $total_gstvalue = 0;
312 for my $order (@orders) {
313 $order = C4::Acquisition::populate_order_with_prices({ order => $order, booksellerid => $booksellerid, ordering => 1 });
314 my $line = get_order_infos( $order, $bookseller);
315 if ( $line->{uncertainprice} ) {
316 $template->param( uncertainprices => 1 );
319 push @books_loop, $line;
321 $foot{$$line{gstrate}}{gstrate} = $$line{gstrate};
322 $foot{$$line{gstrate}}{gstvalue} += $$line{gstvalue};
323 $total_gstvalue += $$line{gstvalue};
324 $foot{$$line{gstrate}}{quantity} += $$line{quantity};
325 $total_quantity += $$line{quantity};
326 $foot{$$line{gstrate}}{totalgste} += $$line{totalgste};
327 $total_gste += $$line{totalgste};
328 $foot{$$line{gstrate}}{totalgsti} += $$line{totalgsti};
329 $total_gsti += $$line{totalgsti};
332 push @book_foot_loop, map {$_} values %foot;
334 # Get cancelled orders
335 my @cancelledorders = GetOrders($basketno, { cancelled => 1 });
336 my @cancelledorders_loop;
337 for my $order (@cancelledorders) {
338 $order = C4::Acquisition::populate_order_with_prices({ order => $order, booksellerid => $booksellerid, ordering => 1 });
339 my $line = get_order_infos( $order, $bookseller);
340 push @cancelledorders_loop, $line;
343 my $contract = GetContract({
344 contractnumber => $basket->{contractnumber}
347 if ($basket->{basketgroupid}){
348 $basketgroup = GetBasketgroup($basket->{basketgroupid});
350 my $borrower= GetMember('borrowernumber' => $loggedinuser);
351 my $budgets = GetBudgetHierarchy;
352 my $has_budgets = 0;
353 foreach my $r (@{$budgets}) {
354 if (!defined $r->{budget_amount} || $r->{budget_amount} == 0) {
355 next;
357 next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
359 $has_budgets = 1;
360 last;
363 $template->param(
364 basketno => $basketno,
365 basket => $basket,
366 basketname => $basket->{'basketname'},
367 basketbranchcode => $basket->{branch},
368 basketnote => $basket->{note},
369 basketbooksellernote => $basket->{booksellernote},
370 basketcontractno => $basket->{contractnumber},
371 basketcontractname => $contract->{contractname},
372 branches_loop => \@branches_loop,
373 creationdate => $basket->{creationdate},
374 authorisedby => $basket->{authorisedby},
375 authorisedbyname => $basket->{authorisedbyname},
376 users_ids => join(':', @basketusers_ids),
377 users => \@basketusers,
378 closedate => $basket->{closedate},
379 estimateddeliverydate=> $estimateddeliverydate,
380 is_standing => $basket->{is_standing},
381 deliveryplace => $basket->{deliveryplace},
382 billingplace => $basket->{billingplace},
383 active => $bookseller->{'active'},
384 booksellerid => $bookseller->{'id'},
385 name => $bookseller->{'name'},
386 books_loop => \@books_loop,
387 book_foot_loop => \@book_foot_loop,
388 cancelledorders_loop => \@cancelledorders_loop,
389 total_quantity => $total_quantity,
390 total_gste => sprintf( "%.2f", $total_gste ),
391 total_gsti => sprintf( "%.2f", $total_gsti ),
392 total_gstvalue => sprintf( "%.2f", $total_gstvalue ),
393 currency => $active_currency->currency,
394 listincgst => $bookseller->{listincgst},
395 basketgroups => $basketgroups,
396 basketgroup => $basketgroup,
397 grouped => $basket->{basketgroupid},
398 # The double negatives and booleans here mean:
399 # "A basket cannot be closed if there are no orders in it or it's a standing order basket."
401 # (The template has another implicit restriction that the order cannot be closed if there
402 # are any orders with uncertain prices.)
403 unclosable => @orders ? $basket->{is_standing} : 1,
404 has_budgets => $has_budgets,
405 duplinbatch => $duplinbatch,
409 sub get_order_infos {
410 my $order = shift;
411 my $bookseller = shift;
412 my $qty = $order->{'quantity'} || 0;
413 if ( !defined $order->{quantityreceived} ) {
414 $order->{quantityreceived} = 0;
416 my $budget = GetBudget($order->{budget_id});
417 my $basket = GetBasket($order->{basketno});
419 my %line = %{ $order };
420 # Don't show unreceived standing orders as received
421 $line{order_received} = ( $qty == $order->{'quantityreceived'} && ( $basket->{is_standing} ? $qty : 1 ) );
422 $line{basketno} = $basketno;
423 $line{budget_name} = $budget->{budget_name};
425 if ( $line{uncertainprice} ) {
426 $line{rrpgste} .= ' (Uncertain)';
428 if ( $line{'title'} ) {
429 my $volume = $order->{'volume'};
430 my $seriestitle = $order->{'seriestitle'};
431 $line{'title'} .= " / $seriestitle" if $seriestitle;
432 $line{'title'} .= " / $volume" if $volume;
435 my $biblionumber = $order->{'biblionumber'};
436 my $countbiblio = CountBiblioInOrders($biblionumber);
437 my $ordernumber = $order->{'ordernumber'};
438 my @subscriptions = GetSubscriptionsId ($biblionumber);
439 my $itemcount = GetItemsCount($biblionumber);
440 my $holds = GetHolds ($biblionumber);
441 my @items = GetItemnumbersFromOrder( $ordernumber );
442 my $itemholds;
443 foreach my $item (@items){
444 my $nb = GetItemHolds($biblionumber, $item);
445 if ($nb){
446 $itemholds += $nb;
449 # 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
450 $line{can_del_bib} = 1 if $countbiblio <= 1 && $itemcount == scalar @items && !(@subscriptions) && !($holds);
451 $line{items} = ($itemcount) - (scalar @items);
452 $line{left_item} = 1 if $line{items} >= 1;
453 $line{left_biblio} = 1 if $countbiblio > 1;
454 $line{biblios} = $countbiblio - 1;
455 $line{left_subscription} = 1 if scalar @subscriptions >= 1;
456 $line{subscriptions} = scalar @subscriptions;
457 ($holds >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
458 $line{left_holds_on_order} = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
459 $line{holds} = $holds;
460 $line{holds_on_order} = $itemholds?$itemholds:$holds if $line{left_holds_on_order};
463 my $suggestion = GetSuggestionInfoFromBiblionumber($line{biblionumber});
464 $line{suggestionid} = $$suggestion{suggestionid};
465 $line{surnamesuggestedby} = $$suggestion{surnamesuggestedby};
466 $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
468 foreach my $key (qw(transferred_from transferred_to)) {
469 if ($line{$key}) {
470 my $order = GetOrder($line{$key});
471 my $bookseller = Koha::Acquisition::Bookseller->fetch({ id => $basket->{booksellerid} });
472 $line{$key} = {
473 order => $order,
474 basket => $basket,
475 bookseller => $bookseller,
476 timestamp => $line{$key . '_timestamp'},
481 return \%line;
484 output_html_with_http_headers $query, $cookie, $template->output;
487 sub edi_close_and_order {
488 my $confirm = $query->param('confirm') || $confirm_pref eq '2';
489 if ($confirm) {
490 my $edi_params = {
491 basketno => $basketno,
492 ean => $ean,
494 if ( $basket->{branch} ) {
495 $edi_params->{branchcode} = $basket->{branch};
497 if ( create_edi_order($edi_params) ) {
498 #$template->param( edifile => 1 );
500 CloseBasket($basketno);
502 # if requested, create basket group, close it and attach the basket
503 if ( $query->param('createbasketgroup') ) {
504 my $branchcode;
505 if ( C4::Context->userenv
506 and C4::Context->userenv->{'branch'}
507 and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET" )
509 $branchcode = C4::Context->userenv->{'branch'};
511 my $basketgroupid = NewBasketgroup(
513 name => $basket->{basketname},
514 booksellerid => $booksellerid,
515 deliveryplace => $branchcode,
516 billingplace => $branchcode,
517 closed => 1,
520 ModBasket(
522 basketno => $basketno,
523 basketgroupid => $basketgroupid
526 print $query->redirect(
527 "/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=$booksellerid&closed=1"
530 else {
531 print $query->redirect(
532 "/cgi-bin/koha/acqui/booksellers.pl?booksellerid=$booksellerid"
535 exit;
537 else {
538 $template->param(
539 edi_confirm => 1,
540 booksellerid => $booksellerid,
541 basketno => $basket->{basketno},
542 basketname => $basket->{basketname},
543 basketgroupname => $basket->{basketname},
545 if ($ean) {
546 $template->param( ean => $ean );
550 return;