3 #script to group (closed) baskets into basket groups for easier order management
4 #written by john.soros@biblibre.com 01/10/2008
6 # Copyright 2008 - 2009 BibLibre SARL
7 # Parts Copyright Catalyst 2010
9 # This file is part of Koha.
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
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.
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.
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 to be printed or exported.
40 The bookseller who we want to display the baskets (and basketgroups) of.
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/;
64 our ($template, $loggedinuser, $cookie)
65 = get_template_and_user({template_name => "acqui/basketgroup.tmpl",
69 flagsrequired => {acquisition => 'group_manage'},
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);
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;
91 sub parseinputbasketgroups {
92 my $booksellerid = shift;
94 my $basketgroups = &GetBasketgroups($booksellerid);
96 foreach my $basket (@$baskets){
100 if(! $basket->{'basketgroupid'} || $basket->{'basketgroupid'} == 0){
103 foreach my $basketgroup (@$basketgroups){
104 if($basket->{'basketgroupid'} == $basketgroup->{'id'}){
106 push(@{$basketgroup->{'basketlist'}}, $basket->{'basketno'});
112 #if the basketgroup doesn't exist yet
113 $basketgroup = $newbasketgroups->{$basket->{'basketgroupid'}} || undef;
114 $basketgroup->{'booksellerid'} = $booksellerid;
116 while($i < scalar @$basketgroups && @$basketgroups[$i]->{'id'} != $basket->{'basketgroupid'}){
119 $basketgroup = @$basketgroups[$i];
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'});
126 $newbasketgroups->{$basket->{'basketgroupid'}} = $basketgroup;
128 if($basketgroup->{'id'}){
129 @$basketgroups[$i] = $basketgroup;
133 return($basketgroups, $newbasketgroups);
137 my $basketno = shift;
138 my $bookseller = shift;
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);
148 $total .= $bookseller->{invoiceprice};
152 #displays all basketgroups and all closed baskets (in their respective groups)
153 sub displaybasketgroups {
154 my $basketgroups = shift;
155 my $bookseller = shift;
157 if (scalar @$basketgroups != 0) {
158 foreach my $basketgroup (@$basketgroups){
161 while($i < scalar(@$baskets)){
162 my $basket = @$baskets[$i];
163 if($basket->{'basketgroupid'} && $basket->{'basketgroupid'} == $basketgroup->{'id'}){
164 $basket->{total} = BasketTotal($basket->{basketno}, $bookseller);
165 push(@{$basketgroup->{'baskets'}}, $basket);
166 splice(@$baskets, $i, 1);
172 $basketgroup -> {'basketsqty'} = $basketsqty;
174 $template->param(basketgroups => $basketgroups);
176 for(my $i=0; $i < scalar @$baskets; ++$i) {
177 if( ! @$baskets[$i]->{'closedate'} ) {
178 splice(@$baskets, $i, 1);
181 @$baskets[$i]->{total} = BasketTotal(@$baskets[$i]->{basketno}, $bookseller);
184 $template->param(baskets => $baskets);
185 $template->param( booksellername => $bookseller ->{'name'});
188 sub printbasketgrouppdf{
189 my ($basketgroupid) = @_;
191 my $pdfformat = C4::Context->preference("OrderPdfFormat");
192 if ($pdfformat eq 'pdfformat::layout3pages' || $pdfformat eq 'pdfformat::layout2pages' || $pdfformat eq 'pdfformat::layout3pagesfr'){
194 eval "require $pdfformat";
201 print $input->header;
202 print $input->start_html; # FIXME Should do a nicer page
203 print "<h1>Invalid PDF Format set</h1>";
204 print "Please go to the systempreferences and set a valid pdfformat";
208 my $basketgroup = GetBasketgroup($basketgroupid);
209 my $bookseller = GetBookSellerFromId($basketgroup->{'booksellerid'});
210 my $baskets = GetBasketsByBasketgroup($basketgroupid);
213 for my $basket (@$baskets) {
215 my @ords = &GetOrders($basket->{basketno});
216 for my $ord (@ords) {
218 next unless ( $ord->{biblionumber} or $ord->{quantity}> 0 );
234 $ord->{rrp} = ConvertCurrency( $ord->{'currency'}, $ord->{rrp} );
235 if ( $bookseller->{'listincgst'} ) {
236 $ord->{rrpgsti} = sprintf( "%.2f", $ord->{rrp} );
237 $ord->{gstgsti} = sprintf( "%.2f", $ord->{gstrate} * 100 );
238 $ord->{rrpgste} = sprintf( "%.2f", $ord->{rrp} / ( 1 + ( $ord->{gstgsti} / 100 ) ) );
239 $ord->{gstgste} = sprintf( "%.2f", $ord->{gstgsti} / ( 1 + ( $ord->{gstgsti} / 100 ) ) );
240 $ord->{ecostgsti} = sprintf( "%.2f", $ord->{ecost} );
241 $ord->{ecostgste} = sprintf( "%.2f", $ord->{ecost} / ( 1 + ( $ord->{gstgsti} / 100 ) ) );
242 $ord->{gstvalue} = sprintf( "%.2f", ( $ord->{ecostgsti} - $ord->{ecostgste} ) * $ord->{quantity});
243 $ord->{totalgste} = sprintf( "%.2f", $ord->{quantity} * $ord->{ecostgste} );
244 $ord->{totalgsti} = sprintf( "%.2f", $ord->{quantity} * $ord->{ecostgsti} );
246 $ord->{rrpgsti} = sprintf( "%.2f", $ord->{rrp} * ( 1 + ( $ord->{gstrate} ) ) );
247 $ord->{rrpgste} = sprintf( "%.2f", $ord->{rrp} );
248 $ord->{gstgsti} = sprintf( "%.2f", $ord->{gstrate} * 100 );
249 $ord->{gstgste} = sprintf( "%.2f", $ord->{gstrate} * 100 );
250 $ord->{ecostgsti} = sprintf( "%.2f", $ord->{ecost} * ( 1 + ( $ord->{gstrate} ) ) );
251 $ord->{ecostgste} = sprintf( "%.2f", $ord->{ecost} );
252 $ord->{gstvalue} = sprintf( "%.2f", ( $ord->{ecostgsti} - $ord->{ecostgste} ) * $ord->{quantity});
253 $ord->{totalgste} = sprintf( "%.2f", $ord->{quantity} * $ord->{ecostgste} );
254 $ord->{totalgsti} = sprintf( "%.2f", $ord->{quantity} * $ord->{ecostgsti} );
256 my $bib = GetBiblioData($ord->{biblionumber});
257 my $itemtypes = GetItemTypes();
260 # 0 1 2 3 4 5 6 7 8 9
261 #isbn, itemtype, author, title, publishercode, quantity, listprice ecost discount gstrate
265 my $marcrecord=eval{MARC::Record::new_from_xml( $ord->{marcxml},'UTF-8' )};
267 if ( C4::Context->preference("marcflavour") eq 'UNIMARC' ) {
268 $en = $marcrecord->subfield( '345', "b" );
269 } elsif ( C4::Context->preference("marcflavour") eq 'MARC21' ) {
270 $en = $marcrecord->subfield( '037', "a" );
275 isbn => ($ord->{isbn} ? $ord->{isbn} : undef),
276 itemtype => ( $ord->{itemtype} and $bib->{itemtype} ? $itemtypes->{$bib->{itemtype}}->{description} : undef ),
277 en => ( $en ? $en : undef ),
279 for my $key ( qw/ gstrate author title itemtype publishercode discount quantity rrpgsti rrpgste gstgsti gstgste ecostgsti ecostgste gstvalue totalgste totalgsti / ) {
280 $ba_order->{$key} = $ord->{$key};
283 push(@ba_orders, $ba_order);
285 $orders{$basket->{basketno}} = \@ba_orders;
287 print $input->header(
288 -type => 'application/pdf',
289 -attachment => ( $basketgroup->{name} || $basketgroupid ) . '.pdf'
291 my $pdf = printpdf($basketgroup, $bookseller, $baskets, \%orders, $bookseller->{gstrate} // C4::Context->preference("gist")) || die "pdf generation failed";
296 my $op = $input->param('op') || 'display';
297 # possible values of $op :
298 # - add : adds a new basketgroup, or edit an open basketgroup, or display a closed basketgroup
299 # - mod_basket : modify an individual basket of the basketgroup
300 # - validate : FIXME dead code
301 # - closeandprint : close and print an closed basketgroup in pdf. called by clicking on "Close and print" button in closed basketgroups list
302 # - print : print a closed basketgroup. called by clicking on "Print" button in closed basketgroups list
303 # - export : export in CSV a closed basketgroup. called by clicking on "Export" button in closed basketgroups list
304 # - delete : delete an open basketgroup. called by clicking on "Delete" button in open basketgroups list
305 # - reopen : reopen a closed basketgroup. called by clicking on "Reopen" button in closed basketgroup list
306 # - attachbasket : save a modified basketgroup, or creates a new basketgroup when a basket is closed. called from basket page
307 # - display : display the list of all basketgroups for a vendor
308 my $booksellerid = $input->param('booksellerid');
309 $template->param(booksellerid => $booksellerid);
311 if ( $op eq "add" ) {
313 # if no param('basketgroupid') is not defined, adds a new basketgroup
314 # else, edit (if it is open) or display (if it is close) the basketgroup basketgroupid
315 # the template will know if basketgroup must be displayed or edited, depending on the value of closed key
319 # FIXME : ungroupedlist does not seem to be used in this file nor in template
320 $template->param( ungroupedlist => 1);
321 my @booksellers = GetBookSeller('');
322 for (my $i=0; $i < scalar @booksellers; $i++) {
323 my $baskets = &GetBasketsByBookseller($booksellers[$i]->{id});
324 for (my $j=0; $j < scalar @$baskets; $j++) {
325 if(! @$baskets[$i]->{closedate} || @$baskets[$i]->{basketgroupid}) {
326 splice(@$baskets, $j, 1);
330 if (scalar @$baskets == 0){
331 splice(@booksellers, $i, 1);
337 my $basketgroupid = $input->param('basketgroupid');
340 my $freedeliveryplace;
341 if ( $basketgroupid ) {
342 # Get the selected baskets in the basketgroup to display them
343 my $selecteds = GetBasketsByBasketgroup($basketgroupid);
344 foreach (@{$selecteds}){
345 $_->{total} = BasketTotal($_->{basketno}, $_);
347 $template->param(basketgroupid => $basketgroupid,
348 selectedbaskets => $selecteds);
350 # Get general informations about the basket group to prefill the form
351 my $basketgroup = GetBasketgroup($basketgroupid);
353 name => $basketgroup->{name},
354 deliverycomment => $basketgroup->{deliverycomment},
355 freedeliveryplace => $basketgroup->{freedeliveryplace},
357 $billingplace = $basketgroup->{billingplace};
358 $deliveryplace = $basketgroup->{deliveryplace};
359 $freedeliveryplace = $basketgroup->{freedeliveryplace};
360 $template->param( closedbg => ($basketgroup ->{'closed'}) ? 1 : 0);
362 $template->param( closedbg => 0);
364 # determine default billing and delivery places depending on librarian homebranch and existing basketgroup data
365 my $borrower = GetMember( ( 'borrowernumber' => $loggedinuser ) );
366 $billingplace = $billingplace || $borrower->{'branchcode'};
367 $deliveryplace = $deliveryplace || $borrower->{'branchcode'};
369 my $branches = C4::Branch::GetBranchesLoop( $billingplace );
370 $template->param( billingplaceloop => $branches );
371 $branches = C4::Branch::GetBranchesLoop( $deliveryplace );
372 $template->param( deliveryplaceloop => $branches );
373 $template->param( booksellerid => $booksellerid );
375 # the template will display a unique basketgroup
376 $template->param(grouping => 1);
377 my $basketgroups = &GetBasketgroups($booksellerid);
378 my $bookseller = &GetBookSellerFromId($booksellerid);
379 my $baskets = &GetBasketsByBookseller($booksellerid);
380 displaybasketgroups($basketgroups, $bookseller, $baskets);
381 } elsif ($op eq 'mod_basket') {
383 # edit an individual basket contained in this basketgroup
385 my $basketno=$input->param('basketno');
386 my $basketgroupid=$input->param('basketgroupid');
387 ModBasket( { basketno => $basketno,
388 basketgroupid => $basketgroupid } );
389 print $input->redirect("basket.pl?basketno=" . $basketno);
390 } elsif ($op eq 'validate') {
395 $template->param( booksellererror => 1);
397 $template->param( booksellerid => $booksellerid );
399 my $baskets = parseinputbaskets($booksellerid);
400 my ($basketgroups, $newbasketgroups) = parseinputbasketgroups($booksellerid, $baskets);
401 foreach my $nbgid (keys %$newbasketgroups){
402 #javascript just picks an ID that's higher than anything else, the ID might not be correct..change it and change all the basket's basketgroupid as well
403 my $bgid = NewBasketgroup($newbasketgroups->{$nbgid});
404 ${$newbasketgroups->{$nbgid}}->{'id'} = $bgid;
405 ${$newbasketgroups->{$nbgid}}->{'oldid'} = $nbgid;
407 foreach my $basket (@$baskets){
408 #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.
409 if ( $basket->{'basketgroupid'} && $newbasketgroups->{$basket->{'basketgroupid'}} ){
410 $basket->{'basketgroupid'} = ${$newbasketgroups->{$basket->{'basketgroupid'}}}->{'id'};
414 foreach my $basketgroup (@$basketgroups){
415 if(! $basketgroup->{'id'}){
416 foreach my $basket (@{$basketgroup->{'baskets'}}){
417 if($input->param('basket'.$basket->{'basketno'}.'changed')){
421 } elsif ($input->param('basketgroup-'.$basketgroup->{'id'}.'-changed')){
422 ModBasketgroup($basketgroup);
425 $basketgroups = &GetBasketgroups($booksellerid);
426 my $bookseller = &GetBookSellerFromId($booksellerid);
427 $baskets = &GetBasketsByBookseller($booksellerid);
428 # keep ungroupedbaskets
430 displaybasketgroups($basketgroups, $bookseller, $baskets);
431 } elsif ( $op eq 'closeandprint') {
433 # close an open basketgroup and generates a pdf
435 my $basketgroupid = $input->param('basketgroupid');
436 CloseBasketgroup($basketgroupid);
437 printbasketgrouppdf($basketgroupid);
439 }elsif ($op eq 'print'){
441 # print a closed basketgroup
443 my $basketgroupid = $input->param('basketgroupid');
444 printbasketgrouppdf($basketgroupid);
446 }elsif ( $op eq "export" ) {
448 # export a closed basketgroup in csv
450 my $basketgroupid = $input->param('basketgroupid');
451 print $input->header(
453 -attachment => 'basketgroup' . $basketgroupid . '.csv',
455 print GetBasketGroupAsCSV( $basketgroupid, $input );
457 }elsif( $op eq "delete"){
459 # delete an closed basketgroup
461 my $basketgroupid = $input->param('basketgroupid');
462 DelBasketgroup($basketgroupid);
463 print $input->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=' . $booksellerid.'&listclosed=1');
464 }elsif ( $op eq 'reopen'){
466 # reopen a closed basketgroup
468 my $basketgroupid = $input->param('basketgroupid');
469 my $booksellerid = $input->param('booksellerid');
470 ReOpenBasketgroup($basketgroupid);
471 my $redirectpath = ((defined $input->param('mode'))&& ($input->param('mode') eq 'singlebg')) ?'/cgi-bin/koha/acqui/basketgroup.pl?op=add&basketgroupid='.$basketgroupid.'&booksellerid='.$booksellerid : '/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=' .$booksellerid.'&listclosed=1';
472 print $input->redirect($redirectpath);
473 } elsif ( $op eq 'attachbasket') {
475 # save a modified basketgroup, or creates a new basketgroup when a basket is closed. called from basket page
478 my $basketgroup = {};
479 my @baskets = $input->param('basket');
480 my $basketgroupid = $input->param('basketgroupid');
481 my $basketgroupname = $input->param('basketgroupname');
482 my $booksellerid = $input->param('booksellerid');
483 my $billingplace = $input->param('billingplace');
484 my $deliveryplace = $input->param('deliveryplace');
485 my $freedeliveryplace = $input->param('freedeliveryplace');
486 my $deliverycomment = $input->param('deliverycomment');
487 my $closedbg = $input->param('closedbg') ? 1 : 0;
488 if ($basketgroupid) {
489 # If we have a basketgroupid we edit the basketgroup
491 name => $basketgroupname,
492 id => $basketgroupid,
493 basketlist => \@baskets,
494 billingplace => $billingplace,
495 deliveryplace => $deliveryplace,
496 freedeliveryplace => $freedeliveryplace,
497 deliverycomment => $deliverycomment,
500 ModBasketgroup($basketgroup);
505 # we create a new basketgroup (whith a closed basket)
507 name => $basketgroupname,
508 booksellerid => $booksellerid,
509 basketlist => \@baskets,
510 billingplace => $billingplace,
511 deliveryplace => $deliveryplace,
512 freedeliveryplace => $freedeliveryplace,
513 deliverycomment => $deliverycomment,
516 $basketgroupid = NewBasketgroup($basketgroup);
518 my $redirectpath = ((defined $input->param('mode')) && ($input->param('mode') eq 'singlebg')) ?'/cgi-bin/koha/acqui/basketgroup.pl?op=add&basketgroupid='.$basketgroupid.'&booksellerid='.$booksellerid : '/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=' . $booksellerid;
519 $redirectpath .= "&listclosed=1" if $closedbg ;
520 print $input->redirect($redirectpath );
523 # no param : display the list of all basketgroups for a given vendor
524 my $basketgroups = &GetBasketgroups($booksellerid);
525 my $bookseller = &GetBookSellerFromId($booksellerid);
526 my $baskets = &GetBasketsByBookseller($booksellerid);
528 displaybasketgroups($basketgroups, $bookseller, $baskets);
530 $template->param(listclosed => ((defined $input->param('listclosed')) && ($input->param('listclosed') eq '1'))? 1:0 );
531 #prolly won't use all these, maybe just use print, the rest can be done inside validate
532 output_html_with_http_headers $input, $cookie, $template->output;