Merge branch 'bug_6554' into 3.14-master
[koha.git] / acqui / basketgroup.pl
1 #!/usr/bin/perl
2
3 #script to group (closed) baskets into basket groups for easier order management
4 #written by john.soros@biblibre.com 01/10/2008
5
6 # Copyright 2008 - 2009 BibLibre SARL
7 # Parts Copyright Catalyst 2010
8 #
9 # This file is part of Koha.
10 #
11 # Koha is free software; you can redistribute it and/or modify it under the
12 # terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
14 # version.
15 #
16 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
17 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License along
21 # with Koha; if not, write to the Free Software Foundation, Inc.,
22 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
24
25 =head1 NAME
26
27 basketgroup.pl
28
29 =head1 DESCRIPTION
30
31  This script lets the user group (closed) baskets into basket groups for easier order management. Note that the grouped baskets have to be from the same bookseller and
32  have to be closed.
33
34 =head1 CGI PARAMETERS
35
36 =over 4
37
38 =item $booksellerid
39
40 The bookseller who we want to display the baskets (and basketgroups) of.
41
42 =back
43
44 =cut
45
46 use strict;
47 use warnings;
48 use Carp;
49
50 use C4::Input;
51 use C4::Auth;
52 use C4::Output;
53 use CGI;
54
55 use C4::Bookseller qw/GetBookSellerFromId/;
56 use C4::Budgets qw/ConvertCurrency/;
57 use C4::Acquisition qw/CloseBasketgroup ReOpenBasketgroup GetOrders GetBasketsByBasketgroup GetBasketsByBookseller ModBasketgroup NewBasketgroup DelBasketgroup GetBasketgroups ModBasket GetBasketgroup GetBasket GetBasketGroupAsCSV/;
58 use C4::Bookseller qw/GetBookSellerFromId/;
59 use C4::Branch qw/GetBranches/;
60 use C4::Members qw/GetMember/;
61
62 our $input=new CGI;
63
64 our ($template, $loggedinuser, $cookie)
65     = get_template_and_user({template_name => "acqui/basketgroup.tmpl",
66                              query => $input,
67                              type => "intranet",
68                              authnotrequired => 0,
69                              flagsrequired => {acquisition => 'group_manage'},
70                              debug => 1,
71                 });
72
73 sub parseinputbaskets {
74     my $booksellerid = shift;
75     my $baskets = &GetBasketsByBookseller($booksellerid);
76     for(my $i=0; $i < scalar @$baskets; ++$i) {
77         if( @$baskets[$i] && ! @$baskets[$i]->{'closedate'} ) {
78             splice(@$baskets, $i, 1);
79             --$i;
80         }
81     }
82     foreach my $basket (@$baskets){
83 #perl DBI uses value "undef" for the mysql "NULL" value, so i need to check everywhere where $basket->{'basketgroupid'} is used for undef ☹
84         $basket->{'basketgroupid'} = $input->param($basket->{'basketno'}.'-group') || undef;
85     }
86     return $baskets;
87 }
88
89
90
91 sub parseinputbasketgroups {
92     my $booksellerid = shift;
93     my $baskets = shift;
94     my $basketgroups = &GetBasketgroups($booksellerid);
95     my $newbasketgroups;
96     foreach my $basket (@$baskets){
97         my $basketgroup;
98         my $i = 0;
99         my $exists;
100         if(! $basket->{'basketgroupid'} || $basket->{'basketgroupid'} == 0){
101             $exists = "true";
102         } else {
103             foreach my $basketgroup (@$basketgroups){
104                 if($basket->{'basketgroupid'} == $basketgroup->{'id'}){
105                     $exists = "true";
106                     push(@{$basketgroup->{'basketlist'}}, $basket->{'basketno'});
107                     last;
108                 }
109             }
110         }
111         if (! $exists){
112 #if the basketgroup doesn't exist yet
113             $basketgroup = $newbasketgroups->{$basket->{'basketgroupid'}} || undef;
114             $basketgroup->{'booksellerid'} = $booksellerid;
115         } else {
116             while($i < scalar @$basketgroups && @$basketgroups[$i]->{'id'} != $basket->{'basketgroupid'}){
117                 ++$i;
118             }
119             $basketgroup = @$basketgroups[$i];
120         }
121         $basketgroup->{'id'}=$basket->{'basketgroupid'};
122         $basketgroup->{'name'}=$input->param('basketgroup-'.$basketgroup->{'id'}.'-name') || "";
123         $basketgroup->{'closed'}= $input->param('basketgroup-'.$basketgroup->{'id'}.'-closed');
124         push(@{$basketgroup->{'basketlist'}}, $basket->{'basketno'});
125         if (! $exists){
126             $newbasketgroups->{$basket->{'basketgroupid'}} = $basketgroup;
127         } else {
128             if($basketgroup->{'id'}){
129                 @$basketgroups[$i] = $basketgroup;
130             }
131         }
132     }
133     return($basketgroups, $newbasketgroups);
134 }
135
136 sub BasketTotal {
137     my $basketno = shift;
138     my $bookseller = shift;
139     my $total = 0;
140     my @orders = GetOrders($basketno);
141     for my $order (@orders){
142         $total = $total + ( $order->{ecost} * $order->{quantity} );
143         if ($bookseller->{invoiceincgst} && ! $bookseller->{listincgst} && ( $bookseller->{gstrate} // C4::Context->preference("gist") )) {
144             my $gst = $bookseller->{gstrate} // C4::Context->preference("gist");
145             $total = $total * ( $gst / 100 +1);
146         }
147     }
148     $total .= $bookseller->{invoiceprice};
149     return $total;
150 }
151
152 #displays all basketgroups and all closed baskets (in their respective groups)
153 sub displaybasketgroups {
154     my $basketgroups = shift;
155     my $bookseller = shift;
156     my $baskets = shift;
157     if (scalar @$basketgroups != 0) {
158         foreach my $basketgroup (@$basketgroups){
159             my $i = 0;
160             while($i < scalar(@$baskets)){
161                 my $basket = @$baskets[$i];
162                 if($basket->{'basketgroupid'} && $basket->{'basketgroupid'} == $basketgroup->{'id'}){
163                     $basket->{total} = BasketTotal($basket->{basketno}, $bookseller);
164                     push(@{$basketgroup->{'baskets'}}, $basket);
165                     splice(@$baskets, $i, 1);
166                     --$i;
167                 }
168                 ++$i;
169             }
170         }
171         $template->param(basketgroups => $basketgroups);
172     }
173     for(my $i=0; $i < scalar @$baskets; ++$i) {
174         if( ! @$baskets[$i]->{'closedate'} ) {
175             splice(@$baskets, $i, 1);
176             --$i;
177         }else{
178             @$baskets[$i]->{total} = BasketTotal(@$baskets[$i]->{basketno}, $bookseller);
179         }
180     }
181     $template->param(baskets => $baskets);
182     $template->param( booksellername => $bookseller ->{'name'});
183 }
184
185 sub printbasketgrouppdf{
186     my ($basketgroupid) = @_;
187     
188     my $pdfformat = C4::Context->preference("OrderPdfFormat");
189     if ($pdfformat eq 'pdfformat::layout3pages' || $pdfformat eq 'pdfformat::layout2pages'){
190         eval {
191         eval "require $pdfformat";
192             import $pdfformat;
193         };
194         if ($@){
195         }
196     }
197     else {
198         print $input->header;  
199         print $input->start_html;  # FIXME Should do a nicer page
200         print "<h1>Invalid PDF Format set</h1>";
201         print "Please go to the systempreferences and set a valid pdfformat";
202         exit;
203     }
204     
205     my $basketgroup = GetBasketgroup($basketgroupid);
206     my $bookseller = GetBookSellerFromId($basketgroup->{'booksellerid'});
207     my $baskets = GetBasketsByBasketgroup($basketgroupid);
208     
209     my %orders;
210     for my $basket (@$baskets) {
211         my @ba_orders;
212         my @ords = &GetOrders($basket->{basketno});
213         for my $ord (@ords) {
214
215             next unless ( $ord->{biblionumber} or $ord->{quantity}> 0 );
216             eval {
217                 require C4::Biblio;
218                 import C4::Biblio;
219             };
220             if ($@){
221                 croak $@;
222             }
223             eval {
224                 require C4::Koha;
225                 import C4::Koha;
226             };
227             if ($@){
228                 croak $@;
229             }
230
231             $ord->{rrp} = ConvertCurrency( $ord->{'currency'}, $ord->{rrp} );
232             if ( $bookseller->{'listincgst'} ) {
233                 $ord->{rrpgsti} = sprintf( "%.2f", $ord->{rrp} );
234                 $ord->{gstgsti} = sprintf( "%.2f", $ord->{gstrate} * 100 );
235                 $ord->{rrpgste} = sprintf( "%.2f", $ord->{rrp} / ( 1 + ( $ord->{gstgsti} / 100 ) ) );
236                 $ord->{gstgste} = sprintf( "%.2f", $ord->{gstgsti} / ( 1 + ( $ord->{gstgsti} / 100 ) ) );
237                 $ord->{ecostgsti} = sprintf( "%.2f", $ord->{ecost} );
238                 $ord->{ecostgste} = sprintf( "%.2f", $ord->{ecost} / ( 1 + ( $ord->{gstgsti} / 100 ) ) );
239                 $ord->{gstvalue} = sprintf( "%.2f", ( $ord->{ecostgsti} - $ord->{ecostgste} ) * $ord->{quantity});
240                 $ord->{totalgste} = sprintf( "%.2f", $ord->{quantity} * $ord->{ecostgste} );
241                 $ord->{totalgsti} = sprintf( "%.2f", $ord->{quantity} * $ord->{ecostgsti} );
242             } else {
243                 $ord->{rrpgsti} = sprintf( "%.2f", $ord->{rrp} * ( 1 + ( $ord->{gstrate} ) ) );
244                 $ord->{rrpgste} = sprintf( "%.2f", $ord->{rrp} );
245                 $ord->{gstgsti} = sprintf( "%.2f", $ord->{gstrate} * 100 );
246                 $ord->{gstgste} = sprintf( "%.2f", $ord->{gstrate} * 100 );
247                 $ord->{ecostgsti} = sprintf( "%.2f", $ord->{ecost} * ( 1 + ( $ord->{gstrate} ) ) );
248                 $ord->{ecostgste} = sprintf( "%.2f", $ord->{ecost} );
249                 $ord->{gstvalue} = sprintf( "%.2f", ( $ord->{ecostgsti} - $ord->{ecostgste} ) * $ord->{quantity});
250                 $ord->{totalgste} = sprintf( "%.2f", $ord->{quantity} * $ord->{ecostgste} );
251                 $ord->{totalgsti} = sprintf( "%.2f", $ord->{quantity} * $ord->{ecostgsti} );
252             }
253             my $bib = GetBiblioData($ord->{biblionumber});
254             my $itemtypes = GetItemTypes();
255
256             #FIXME DELETE ME
257             # 0      1        2        3         4            5         6       7      8        9
258             #isbn, itemtype, author, title, publishercode, quantity, listprice ecost discount gstrate
259
260             # Editor Number
261             my $en;
262             my $marcrecord=eval{MARC::Record::new_from_xml( $ord->{marcxml},'UTF-8' )};
263             if ($marcrecord){
264                 if ( C4::Context->preference("marcflavour") eq 'UNIMARC' ) {
265                     $en = $marcrecord->subfield( '345', "b" );
266                 } elsif ( C4::Context->preference("marcflavour") eq 'MARC21' ) {
267                     $en = $marcrecord->subfield( '037', "a" );
268                 }
269             }
270
271             my $ba_order = {
272                 isbn => ($ord->{isbn} ? $ord->{isbn} : undef),
273                 itemtype => ( $ord->{itemtype} and $bib->{itemtype} ? $itemtypes->{$bib->{itemtype}}->{description} : undef ),
274                 en => ( $en ? $en : undef ),
275             };
276             for my $key ( qw/ gstrate author title itemtype publishercode discount quantity rrpgsti rrpgste gstgsti gstgste ecostgsti ecostgste gstvalue totalgste totalgsti / ) {
277                 $ba_order->{$key} = $ord->{$key};
278             }
279
280             push(@ba_orders, $ba_order);
281         }
282         $orders{$basket->{basketno}} = \@ba_orders;
283     }
284     print $input->header(
285         -type       => 'application/pdf',
286         -attachment => ( $basketgroup->{name} || $basketgroupid ) . '.pdf'
287     );
288     my $pdf = printpdf($basketgroup, $bookseller, $baskets, \%orders, $bookseller->{gstrate} // C4::Context->preference("gist")) || die "pdf generation failed";
289     print $pdf;
290
291 }
292
293 my $op = $input->param('op') || 'display';
294 my $booksellerid = $input->param('booksellerid');
295 $template->param(booksellerid => $booksellerid);
296
297 if ( $op eq "add" ) {
298     if(! $booksellerid){
299         $template->param( ungroupedlist => 1);
300         my @booksellers = GetBookSeller('');
301        for (my $i=0; $i < scalar @booksellers; $i++) {
302             my $baskets = &GetBasketsByBookseller($booksellers[$i]->{id});
303             for (my $j=0; $j < scalar @$baskets; $j++) {
304                 if(! @$baskets[$i]->{closedate} || @$baskets[$i]->{basketgroupid}) {
305                     splice(@$baskets, $j, 1);
306                     $j--;
307                 }
308             }
309             if (scalar @$baskets == 0){
310                 splice(@booksellers, $i, 1);
311                 $i--;
312             }
313         }
314     } else {
315         my $basketgroupid = $input->param('basketgroupid');
316         my $billingplace;
317         my $deliveryplace;
318         my $freedeliveryplace;
319         if ( $basketgroupid ) {
320             # Get the selected baskets in the basketgroup to display them
321             my $selecteds = GetBasketsByBasketgroup($basketgroupid);
322             foreach (@{$selecteds}){
323                 $_->{total} = BasketTotal($_->{basketno}, $_);
324             }
325             $template->param(basketgroupid => $basketgroupid,
326                              selectedbaskets => $selecteds);
327
328             # Get general informations about the basket group to prefill the form
329             my $basketgroup = GetBasketgroup($basketgroupid);
330             $template->param(
331                 name            => $basketgroup->{name},
332                 deliverycomment => $basketgroup->{deliverycomment},
333                 freedeliveryplace => $basketgroup->{freedeliveryplace},
334             );
335             $billingplace  = $basketgroup->{billingplace};
336             $deliveryplace = $basketgroup->{deliveryplace};
337             $freedeliveryplace = $basketgroup->{freedeliveryplace};
338         }
339
340         # determine default billing and delivery places depending on librarian homebranch and existing basketgroup data
341         my $borrower = GetMember( ( 'borrowernumber' => $loggedinuser ) );
342         $billingplace  = $billingplace  || $borrower->{'branchcode'};
343         $deliveryplace = $deliveryplace || $borrower->{'branchcode'};
344
345         my $branches = C4::Branch::GetBranchesLoop( $billingplace );
346         $template->param( billingplaceloop => $branches );
347         $branches = C4::Branch::GetBranchesLoop( $deliveryplace );
348         $template->param( deliveryplaceloop => $branches );
349
350         $template->param( booksellerid => $booksellerid );
351     }
352     $template->param(grouping => 1);
353     my $basketgroups = &GetBasketgroups($booksellerid);
354     my $bookseller = &GetBookSellerFromId($booksellerid);
355     my $baskets = &GetBasketsByBookseller($booksellerid);
356
357     displaybasketgroups($basketgroups, $bookseller, $baskets);
358 } elsif ($op eq 'mod_basket') {
359 #we want to modify an individual basket's group
360   my $basketno=$input->param('basketno');
361   my $basketgroupid=$input->param('basketgroupid');
362   ModBasket( { basketno => $basketno,
363                          basketgroupid => $basketgroupid } );
364   print $input->redirect("basket.pl?basketno=" . $basketno);
365 } elsif ($op eq 'validate') {
366     if(! $booksellerid){
367         $template->param( booksellererror => 1);
368     } else {
369         $template->param( booksellerid => $booksellerid );
370     }
371     my $baskets = parseinputbaskets($booksellerid);
372     my ($basketgroups, $newbasketgroups) = parseinputbasketgroups($booksellerid, $baskets);
373     foreach my $nbgid (keys %$newbasketgroups){
374 #javascript just picks an ID that's higher than anything else, the ID might not be correct..chenge it and change all the basket's basketgroupid as well
375         my $bgid = NewBasketgroup($newbasketgroups->{$nbgid});
376         ${$newbasketgroups->{$nbgid}}->{'id'} = $bgid;
377         ${$newbasketgroups->{$nbgid}}->{'oldid'} = $nbgid;
378     }
379     foreach my $basket (@$baskets){
380 #if the basket was added to a new basketgroup, first change the groupid to the groupid of the basket in mysql, because it contains the id from javascript otherwise.
381         if ( $basket->{'basketgroupid'} && $newbasketgroups->{$basket->{'basketgroupid'}} ){
382             $basket->{'basketgroupid'} = ${$newbasketgroups->{$basket->{'basketgroupid'}}}->{'id'};
383         }
384         ModBasket($basket);
385     }
386     foreach my $basketgroup (@$basketgroups){
387         if(! $basketgroup->{'id'}){
388             foreach my $basket (@{$basketgroup->{'baskets'}}){
389                 if($input->param('basket'.$basket->{'basketno'}.'changed')){
390                     ModBasket($basket);
391                 }
392             }
393         } elsif ($input->param('basketgroup-'.$basketgroup->{'id'}.'-changed')){
394             ModBasketgroup($basketgroup);
395         }
396     }
397     $basketgroups = &GetBasketgroups($booksellerid);
398     my $bookseller = &GetBookSellerFromId($booksellerid);
399     $baskets = &GetBasketsByBookseller($booksellerid);
400
401     displaybasketgroups($basketgroups, $bookseller, $baskets);
402 } elsif ( $op eq 'closeandprint') {
403     my $basketgroupid = $input->param('basketgroupid');
404     
405     CloseBasketgroup($basketgroupid);
406     
407     printbasketgrouppdf($basketgroupid);
408     exit;
409 }elsif ($op eq 'print'){
410     my $basketgroupid = $input->param('basketgroupid');
411     
412     printbasketgrouppdf($basketgroupid);
413     exit;
414 }elsif ( $op eq "export" ) {
415     my $basketgroupid = $input->param('basketgroupid');
416     print $input->header(
417         -type       => 'text/csv',
418         -attachment => 'basketgroup' . $basketgroupid . '.csv',
419     );
420     print GetBasketGroupAsCSV( $basketgroupid, $input );
421     exit;
422 }elsif( $op eq "delete"){
423     my $basketgroupid = $input->param('basketgroupid');
424     DelBasketgroup($basketgroupid);
425     print $input->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=' . $booksellerid);
426     
427 }elsif ( $op eq 'reopen'){
428     my $basketgroupid   = $input->param('basketgroupid');
429     my $booksellerid    = $input->param('booksellerid');
430     
431     ReOpenBasketgroup($basketgroupid);
432         
433     print $input->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=' . $booksellerid . '#closed');
434     
435 } elsif ( $op eq 'attachbasket') {
436     
437     # Getting parameters
438     my $basketgroup       = {};
439     my @baskets           = $input->param('basket');
440     my $basketgroupid     = $input->param('basketgroupid');
441     my $basketgroupname   = $input->param('basketgroupname');
442     my $booksellerid      = $input->param('booksellerid');
443     my $billingplace      = $input->param('billingplace');
444     my $deliveryplace     = $input->param('deliveryplace');
445     my $freedeliveryplace = $input->param('freedeliveryplace');
446     my $deliverycomment   = $input->param('deliverycomment');
447     my $close             = $input->param('close') ? 1 : 0;
448     # If we got a basketgroupname, we create a basketgroup
449     if ($basketgroupid) {
450         $basketgroup = {
451               name              => $basketgroupname,
452               id                => $basketgroupid,
453               basketlist        => \@baskets,
454               billingplace      => $billingplace,
455               deliveryplace     => $deliveryplace,
456               freedeliveryplace => $freedeliveryplace,
457               deliverycomment   => $deliverycomment,
458               closed            => $close,
459         };
460         ModBasketgroup($basketgroup);
461         if($close){
462             
463         }
464     }else{
465         $basketgroup = {
466             name              => $basketgroupname,
467             booksellerid      => $booksellerid,
468             basketlist        => \@baskets,
469             billingplace      => $billingplace,
470             deliveryplace     => $deliveryplace,
471             freedeliveryplace => $freedeliveryplace,
472             deliverycomment   => $deliverycomment,
473             closed            => $close,
474         };
475         $basketgroupid = NewBasketgroup($basketgroup);
476     }
477    
478     my $url = '/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=' . $booksellerid;
479     $url .= "&closed=1" if ($input->param("closed")); 
480     print $input->redirect($url);
481     
482 }else{
483     my $basketgroups = &GetBasketgroups($booksellerid);
484     my $bookseller = &GetBookSellerFromId($booksellerid);
485     my $baskets = &GetBasketsByBookseller($booksellerid);
486
487     displaybasketgroups($basketgroups, $bookseller, $baskets);
488 }
489 $template->param(closed => $input->param("closed"));
490 #prolly won't use all these, maybe just use print, the rest can be done inside validate
491 output_html_with_http_headers $input, $cookie, $template->output;