Bug 13726: Make Koha::Acq::Bookseller using Koha::Object
[koha.git] / acqui / basket.pl
1 #!/usr/bin/perl
2
3 #script to show display basket of orders
4
5 # Copyright 2000 - 2004 Katipo
6 # Copyright 2008 - 2009 BibLibre SARL
7 #
8 # This file is part of Koha.
9 #
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.
14 #
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.
19 #
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>.
22
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 );
43
44 =head1 NAME
45
46 basket.pl
47
48 =head1 DESCRIPTION
49
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.
53
54 =head1 CGI PARAMETERS
55
56 =over 4
57
58 =item $basketno
59
60 The basket number.
61
62 =item booksellerid
63
64 the supplier this script have to display the basket.
65
66 =item order
67
68 =back
69
70 =cut
71
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');
77
78 my ( $template, $loggedinuser, $cookie, $userflags ) = get_template_and_user(
79     {
80         template_name   => "acqui/basket.tt",
81         query           => $query,
82         type            => "intranet",
83         authnotrequired => 0,
84         flagsrequired   => { acquisition => 'order_manage' },
85         debug           => 1,
86     }
87 );
88
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));
96
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,
104     );
105     output_html_with_http_headers $query, $cookie, $template->output;
106     exit;
107 }
108
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';
114
115 my $confirm_pref= C4::Context->preference("BasketConfirmations") || '1';
116 $template->param( skip_confirm_reopen => 1) if $confirm_pref eq '2';
117
118 my @messages;
119
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});
127     }
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};
147             }
148             if ($error) {
149                 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
150                                          title=> $myorder->{'title'},
151                                          author=> $myorder->{'author'},
152                                          othererror=> $error};
153             }
154         }
155         $template->param( cannotdelbiblios => \@cannotdelbiblios );
156     }
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',
166     );
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' );
172     };
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' };
179     }
180
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'};
194             }
195             my $basketgroupid = NewBasketgroup( { name => $basket->{basketname},
196                             booksellerid => $booksellerid,
197                             deliveryplace => $branchcode,
198                             billingplace => $branchcode,
199                             closed => 1,
200                             });
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);
206         }
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'},
215     );
216     }
217 } elsif ($op eq 'reopen') {
218     ReopenBasket($query->param('basketno'));
219     print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
220 }
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
235     });
236     print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
237     exit;
238 }
239
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;
253             }
254         }
255
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,
261             };
262         }
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};
272             }
273             push @branches_loop, {
274                 branchcode => $branch->{branchcode},
275                 branchname => $branch->{branchname},
276                 selected => $selected
277             };
278         }
279     }
280
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;
290             }
291         }
292     }
293
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 );
300     }
301
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};
309
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;
315     }
316
317     my $active_currency = Koha::Acquisition::Currencies->get_active;
318
319     my @orders = GetOrders( $basketno );
320     my @books_loop;
321
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 );
332         }
333
334         $line->{tax_rate} = $line->{tax_rate_on_ordering};
335         $line->{tax_value} = $line->{tax_value_on_ordering};
336
337         push @books_loop, $line;
338
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};
348     }
349
350     push @book_foot_loop, map {$_} values %foot;
351
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;
358     }
359
360     my $contract = GetContract({
361         contractnumber => $basket->{contractnumber}
362     });
363
364     if ($basket->{basketgroupid}){
365         $basketgroup = GetBasketgroup($basket->{basketgroupid});
366     }
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;
373         }
374         next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
375
376         $has_budgets = 1;
377         last;
378     }
379
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."
417         #
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,
423     );
424 }
425
426 $template->param( messages => \@messages );
427 output_html_with_http_headers $query, $cookie, $template->output;
428
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;
435     }
436     my $budget = GetBudget($order->{budget_id});
437     my $basket = GetBasket($order->{basketno});
438
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};
444
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};
449
450     if ( $line{uncertainprice} ) {
451         $line{rrp_tax_excluded} .= ' (Uncertain)';
452     }
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;
458     }
459
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;
473         }
474     }
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};
487
488
489     my $suggestion   = GetSuggestionInfoFromBiblionumber($line{biblionumber});
490     $line{suggestionid}         = $$suggestion{suggestionid};
491     $line{surnamesuggestedby}   = $$suggestion{surnamesuggestedby};
492     $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
493
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'},
503             };
504         }
505     }
506
507     return \%line;
508 }
509
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,
516             };
517             if ( $basket->{branch} ) {
518                 $edi_params->{branchcode} = $basket->{branch};
519             }
520             if ( create_edi_order($edi_params) ) {
521                 #$template->param( edifile => 1 );
522             }
523         CloseBasket($basketno);
524
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" )
531             {
532                 $branchcode = C4::Context->userenv->{'branch'};
533             }
534             my $basketgroupid = NewBasketgroup(
535                 {
536                     name          => $basket->{basketname},
537                     booksellerid  => $booksellerid,
538                     deliveryplace => $branchcode,
539                     billingplace  => $branchcode,
540                     closed        => 1,
541                 }
542             );
543             ModBasket(
544                 {
545                     basketno      => $basketno,
546                     basketgroupid => $basketgroupid
547                 }
548             );
549             print $query->redirect(
550 "/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=$booksellerid&closed=1"
551             );
552         }
553         else {
554             print $query->redirect(
555                 "/cgi-bin/koha/acqui/booksellers.pl?booksellerid=$booksellerid"
556             );
557         }
558         exit;
559     }
560     else {
561         $template->param(
562             edi_confirm     => 1,
563             booksellerid    => $booksellerid,
564             basketno        => $basket->{basketno},
565             basketname      => $basket->{basketname},
566             basketgroupname => $basket->{basketname},
567         );
568         if ($ean) {
569             $template->param( ean => $ean );
570         }
571
572     }
573     return;
574 }