Bug 9806 : Add new columns to basket groups lists
[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 GetBranchName/;
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             $basketgroup -> {'billingplacename'} = GetBranchName($basketgroup -> {'billingplace'});
160             $basketgroup -> {'deliveryplacename'} = GetBranchName($basketgroup -> {'deliveryplace'});
161             my $i = 0;
162             my $basketsqty = 0;
163             while($i < scalar(@$baskets)){
164                 my $basket = @$baskets[$i];
165                 if($basket->{'basketgroupid'} && $basket->{'basketgroupid'} == $basketgroup->{'id'}){
166                     $basket->{total} = BasketTotal($basket->{basketno}, $bookseller);
167                     push(@{$basketgroup->{'baskets'}}, $basket);
168                     splice(@$baskets, $i, 1);
169                     ++$basketsqty;
170                     --$i;
171                 }
172                 ++$i;
173             }
174             $basketgroup -> {'basketsqty'} = $basketsqty;
175         }
176         $template->param(basketgroups => $basketgroups);
177     }
178     for(my $i=0; $i < scalar @$baskets; ++$i) {
179         if( ! @$baskets[$i]->{'closedate'} ) {
180             splice(@$baskets, $i, 1);
181             --$i;
182         }else{
183             @$baskets[$i]->{total} = BasketTotal(@$baskets[$i]->{basketno}, $bookseller);
184         }
185     }
186     $template->param(baskets => $baskets);
187     $template->param( booksellername => $bookseller ->{'name'});
188 }
189
190 sub printbasketgrouppdf{
191     my ($basketgroupid) = @_;
192     
193     my $pdfformat = C4::Context->preference("OrderPdfFormat");
194     if ($pdfformat eq 'pdfformat::layout3pages' || $pdfformat eq 'pdfformat::layout2pages'){
195         eval {
196         eval "require $pdfformat";
197             import $pdfformat;
198         };
199         if ($@){
200         }
201     }
202     else {
203         print $input->header;  
204         print $input->start_html;  # FIXME Should do a nicer page
205         print "<h1>Invalid PDF Format set</h1>";
206         print "Please go to the systempreferences and set a valid pdfformat";
207         exit;
208     }
209     
210     my $basketgroup = GetBasketgroup($basketgroupid);
211     my $bookseller = GetBookSellerFromId($basketgroup->{'booksellerid'});
212     my $baskets = GetBasketsByBasketgroup($basketgroupid);
213     
214     my %orders;
215     for my $basket (@$baskets) {
216         my @ba_orders;
217         my @ords = &GetOrders($basket->{basketno});
218         for my $ord (@ords) {
219
220             next unless ( $ord->{biblionumber} or $ord->{quantity}> 0 );
221             eval {
222                 require C4::Biblio;
223                 import C4::Biblio;
224             };
225             if ($@){
226                 croak $@;
227             }
228             eval {
229                 require C4::Koha;
230                 import C4::Koha;
231             };
232             if ($@){
233                 croak $@;
234             }
235
236             $ord->{rrp} = ConvertCurrency( $ord->{'currency'}, $ord->{rrp} );
237             if ( $bookseller->{'listincgst'} ) {
238                 $ord->{rrpgsti} = sprintf( "%.2f", $ord->{rrp} );
239                 $ord->{gstgsti} = sprintf( "%.2f", $ord->{gstrate} * 100 );
240                 $ord->{rrpgste} = sprintf( "%.2f", $ord->{rrp} / ( 1 + ( $ord->{gstgsti} / 100 ) ) );
241                 $ord->{gstgste} = sprintf( "%.2f", $ord->{gstgsti} / ( 1 + ( $ord->{gstgsti} / 100 ) ) );
242                 $ord->{ecostgsti} = sprintf( "%.2f", $ord->{ecost} );
243                 $ord->{ecostgste} = sprintf( "%.2f", $ord->{ecost} / ( 1 + ( $ord->{gstgsti} / 100 ) ) );
244                 $ord->{gstvalue} = sprintf( "%.2f", ( $ord->{ecostgsti} - $ord->{ecostgste} ) * $ord->{quantity});
245                 $ord->{totalgste} = sprintf( "%.2f", $ord->{quantity} * $ord->{ecostgste} );
246                 $ord->{totalgsti} = sprintf( "%.2f", $ord->{quantity} * $ord->{ecostgsti} );
247             } else {
248                 $ord->{rrpgsti} = sprintf( "%.2f", $ord->{rrp} * ( 1 + ( $ord->{gstrate} ) ) );
249                 $ord->{rrpgste} = sprintf( "%.2f", $ord->{rrp} );
250                 $ord->{gstgsti} = sprintf( "%.2f", $ord->{gstrate} * 100 );
251                 $ord->{gstgste} = sprintf( "%.2f", $ord->{gstrate} * 100 );
252                 $ord->{ecostgsti} = sprintf( "%.2f", $ord->{ecost} * ( 1 + ( $ord->{gstrate} ) ) );
253                 $ord->{ecostgste} = sprintf( "%.2f", $ord->{ecost} );
254                 $ord->{gstvalue} = sprintf( "%.2f", ( $ord->{ecostgsti} - $ord->{ecostgste} ) * $ord->{quantity});
255                 $ord->{totalgste} = sprintf( "%.2f", $ord->{quantity} * $ord->{ecostgste} );
256                 $ord->{totalgsti} = sprintf( "%.2f", $ord->{quantity} * $ord->{ecostgsti} );
257             }
258             my $bib = GetBiblioData($ord->{biblionumber});
259             my $itemtypes = GetItemTypes();
260
261             #FIXME DELETE ME
262             # 0      1        2        3         4            5         6       7      8        9
263             #isbn, itemtype, author, title, publishercode, quantity, listprice ecost discount gstrate
264
265             # Editor Number
266             my $en;
267             my $marcrecord=eval{MARC::Record::new_from_xml( $ord->{marcxml},'UTF-8' )};
268             if ($marcrecord){
269                 if ( C4::Context->preference("marcflavour") eq 'UNIMARC' ) {
270                     $en = $marcrecord->subfield( '345', "b" );
271                 } elsif ( C4::Context->preference("marcflavour") eq 'MARC21' ) {
272                     $en = $marcrecord->subfield( '037', "a" );
273                 }
274             }
275
276             my $ba_order = {
277                 isbn => ($ord->{isbn} ? $ord->{isbn} : undef),
278                 itemtype => ( $ord->{itemtype} and $bib->{itemtype} ? $itemtypes->{$bib->{itemtype}}->{description} : undef ),
279                 en => ( $en ? $en : undef ),
280             };
281             for my $key ( qw/ gstrate author title itemtype publishercode discount quantity rrpgsti rrpgste gstgsti gstgste ecostgsti ecostgste gstvalue totalgste totalgsti / ) {
282                 $ba_order->{$key} = $ord->{$key};
283             }
284
285             push(@ba_orders, $ba_order);
286         }
287         $orders{$basket->{basketno}} = \@ba_orders;
288     }
289     print $input->header(
290         -type       => 'application/pdf',
291         -attachment => ( $basketgroup->{name} || $basketgroupid ) . '.pdf'
292     );
293     my $pdf = printpdf($basketgroup, $bookseller, $baskets, \%orders, $bookseller->{gstrate} // C4::Context->preference("gist")) || die "pdf generation failed";
294     print $pdf;
295
296 }
297
298 my $op = $input->param('op') || 'display';
299 my $booksellerid = $input->param('booksellerid');
300 $template->param(booksellerid => $booksellerid);
301
302 if ( $op eq "add" ) {
303     if(! $booksellerid){
304         $template->param( ungroupedlist => 1);
305         my @booksellers = GetBookSeller('');
306        for (my $i=0; $i < scalar @booksellers; $i++) {
307             my $baskets = &GetBasketsByBookseller($booksellers[$i]->{id});
308             for (my $j=0; $j < scalar @$baskets; $j++) {
309                 if(! @$baskets[$i]->{closedate} || @$baskets[$i]->{basketgroupid}) {
310                     splice(@$baskets, $j, 1);
311                     $j--;
312                 }
313             }
314             if (scalar @$baskets == 0){
315                 splice(@booksellers, $i, 1);
316                 $i--;
317             }
318         }
319     } else {
320         my $basketgroupid = $input->param('basketgroupid');
321         my $billingplace;
322         my $deliveryplace;
323         my $freedeliveryplace;
324         if ( $basketgroupid ) {
325             # Get the selected baskets in the basketgroup to display them
326             my $selecteds = GetBasketsByBasketgroup($basketgroupid);
327             foreach (@{$selecteds}){
328                 $_->{total} = BasketTotal($_->{basketno}, $_);
329             }
330             $template->param(basketgroupid => $basketgroupid,
331                              selectedbaskets => $selecteds);
332
333             # Get general informations about the basket group to prefill the form
334             my $basketgroup = GetBasketgroup($basketgroupid);
335             $template->param(
336                 name            => $basketgroup->{name},
337                 deliverycomment => $basketgroup->{deliverycomment},
338                 freedeliveryplace => $basketgroup->{freedeliveryplace},
339             );
340             $billingplace  = $basketgroup->{billingplace};
341             $deliveryplace = $basketgroup->{deliveryplace};
342             $freedeliveryplace = $basketgroup->{freedeliveryplace};
343         }
344
345         # determine default billing and delivery places depending on librarian homebranch and existing basketgroup data
346         my $borrower = GetMember( ( 'borrowernumber' => $loggedinuser ) );
347         $billingplace  = $billingplace  || $borrower->{'branchcode'};
348         $deliveryplace = $deliveryplace || $borrower->{'branchcode'};
349
350         my $branches = C4::Branch::GetBranchesLoop( $billingplace );
351         $template->param( billingplaceloop => $branches );
352         $branches = C4::Branch::GetBranchesLoop( $deliveryplace );
353         $template->param( deliveryplaceloop => $branches );
354
355         $template->param( booksellerid => $booksellerid );
356     }
357     $template->param(grouping => 1);
358     my $basketgroups = &GetBasketgroups($booksellerid);
359     my $bookseller = &GetBookSellerFromId($booksellerid);
360     my $baskets = &GetBasketsByBookseller($booksellerid);
361
362     displaybasketgroups($basketgroups, $bookseller, $baskets);
363 } elsif ($op eq 'mod_basket') {
364 #we want to modify an individual basket's group
365   my $basketno=$input->param('basketno');
366   my $basketgroupid=$input->param('basketgroupid');
367   ModBasket( { basketno => $basketno,
368                          basketgroupid => $basketgroupid } );
369   print $input->redirect("basket.pl?basketno=" . $basketno);
370 } elsif ($op eq 'validate') {
371     if(! $booksellerid){
372         $template->param( booksellererror => 1);
373     } else {
374         $template->param( booksellerid => $booksellerid );
375     }
376     my $baskets = parseinputbaskets($booksellerid);
377     my ($basketgroups, $newbasketgroups) = parseinputbasketgroups($booksellerid, $baskets);
378     foreach my $nbgid (keys %$newbasketgroups){
379 #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
380         my $bgid = NewBasketgroup($newbasketgroups->{$nbgid});
381         ${$newbasketgroups->{$nbgid}}->{'id'} = $bgid;
382         ${$newbasketgroups->{$nbgid}}->{'oldid'} = $nbgid;
383     }
384     foreach my $basket (@$baskets){
385 #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.
386         if ( $basket->{'basketgroupid'} && $newbasketgroups->{$basket->{'basketgroupid'}} ){
387             $basket->{'basketgroupid'} = ${$newbasketgroups->{$basket->{'basketgroupid'}}}->{'id'};
388         }
389         ModBasket($basket);
390     }
391     foreach my $basketgroup (@$basketgroups){
392         if(! $basketgroup->{'id'}){
393             foreach my $basket (@{$basketgroup->{'baskets'}}){
394                 if($input->param('basket'.$basket->{'basketno'}.'changed')){
395                     ModBasket($basket);
396                 }
397             }
398         } elsif ($input->param('basketgroup-'.$basketgroup->{'id'}.'-changed')){
399             ModBasketgroup($basketgroup);
400         }
401     }
402     $basketgroups = &GetBasketgroups($booksellerid);
403     my $bookseller = &GetBookSellerFromId($booksellerid);
404     $baskets = &GetBasketsByBookseller($booksellerid);
405
406     displaybasketgroups($basketgroups, $bookseller, $baskets);
407 } elsif ( $op eq 'closeandprint') {
408     my $basketgroupid = $input->param('basketgroupid');
409     
410     CloseBasketgroup($basketgroupid);
411     
412     printbasketgrouppdf($basketgroupid);
413     exit;
414 }elsif ($op eq 'print'){
415     my $basketgroupid = $input->param('basketgroupid');
416     
417     printbasketgrouppdf($basketgroupid);
418     exit;
419 }elsif ( $op eq "export" ) {
420     my $basketgroupid = $input->param('basketgroupid');
421     print $input->header(
422         -type       => 'text/csv',
423         -attachment => 'basketgroup' . $basketgroupid . '.csv',
424     );
425     print GetBasketGroupAsCSV( $basketgroupid, $input );
426     exit;
427 }elsif( $op eq "delete"){
428     my $basketgroupid = $input->param('basketgroupid');
429     DelBasketgroup($basketgroupid);
430     print $input->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=' . $booksellerid);
431     
432 }elsif ( $op eq 'reopen'){
433     my $basketgroupid   = $input->param('basketgroupid');
434     my $booksellerid    = $input->param('booksellerid');
435     
436     ReOpenBasketgroup($basketgroupid);
437         
438     print $input->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=' . $booksellerid . '#closed');
439     
440 } elsif ( $op eq 'attachbasket') {
441     
442     # Getting parameters
443     my $basketgroup       = {};
444     my @baskets           = $input->param('basket');
445     my $basketgroupid     = $input->param('basketgroupid');
446     my $basketgroupname   = $input->param('basketgroupname');
447     my $booksellerid      = $input->param('booksellerid');
448     my $billingplace      = $input->param('billingplace');
449     my $deliveryplace     = $input->param('deliveryplace');
450     my $freedeliveryplace = $input->param('freedeliveryplace');
451     my $deliverycomment   = $input->param('deliverycomment');
452     my $close             = $input->param('close') ? 1 : 0;
453     # If we got a basketgroupname, we create a basketgroup
454     if ($basketgroupid) {
455         $basketgroup = {
456               name              => $basketgroupname,
457               id                => $basketgroupid,
458               basketlist        => \@baskets,
459               billingplace      => $billingplace,
460               deliveryplace     => $deliveryplace,
461               freedeliveryplace => $freedeliveryplace,
462               deliverycomment   => $deliverycomment,
463               closed            => $close,
464         };
465         ModBasketgroup($basketgroup);
466         if($close){
467             
468         }
469     }else{
470         $basketgroup = {
471             name              => $basketgroupname,
472             booksellerid      => $booksellerid,
473             basketlist        => \@baskets,
474             billingplace      => $billingplace,
475             deliveryplace     => $deliveryplace,
476             freedeliveryplace => $freedeliveryplace,
477             deliverycomment   => $deliverycomment,
478             closed            => $close,
479         };
480         $basketgroupid = NewBasketgroup($basketgroup);
481     }
482    
483     my $url = '/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=' . $booksellerid;
484     $url .= "&closed=1" if ($input->param("closed")); 
485     print $input->redirect($url);
486     
487 }else{
488     my $basketgroups = &GetBasketgroups($booksellerid);
489     my $bookseller = &GetBookSellerFromId($booksellerid);
490     my $baskets = &GetBasketsByBookseller($booksellerid);
491
492     displaybasketgroups($basketgroups, $bookseller, $baskets);
493 }
494 $template->param(closed => $input->param("closed"));
495 #prolly won't use all these, maybe just use print, the rest can be done inside validate
496 output_html_with_http_headers $input, $cookie, $template->output;