From 7e1077b51bbdd13e4a8032f73b286b1ea61bb288 Mon Sep 17 00:00:00 2001 From: Paul Poulain Date: Tue, 5 Apr 2011 12:16:43 +0200 Subject: [PATCH] Bug 5961: Filling a basket from the reservoir creating a form order to import all records from a staged file at once. All lines of the choosen staged file will be created with the same budget and item (if applicable depending on AcqCreateItem syspref) BibLibre bug #4007 Signed-off-by: Katrin Fischer Bug 5961: follow-up : use matchingrules selected in manage-stage-import instead of FindDuplicate Now, bulk importing the iso2709 result in adding biblio or connecting to an existing one Signed-off-by: Katrin Fischer BZ5961: follow-up, save currency Cait noticed that currency was not saved properly in the aqorders table. note a surprise, as the 3.2 had no currency feature and 5961 had been written against 3.2 anyway, this patch fixes the problem Signed-off-by: Katrin Fischer Signed-off-by: Chris Cormack --- C4/Biblio.pm | 68 ++++ acqui/addorderiso2709.pl | 372 ++++++++++-------- .../en/modules/acqui/addorderiso2709.tmpl | 141 ++++++- 3 files changed, 408 insertions(+), 173 deletions(-) diff --git a/C4/Biblio.pm b/C4/Biblio.pm index 7b72411c57..7be1b3e22e 100755 --- a/C4/Biblio.pm +++ b/C4/Biblio.pm @@ -79,6 +79,8 @@ BEGIN { &GetUsedMarcStructure &GetXmlBiblio &GetCOinSBiblio + &GetMarcPrice + &GetMarcQuantity &GetAuthorisedValueDesc &GetMarcStructure @@ -1243,6 +1245,72 @@ sub GetCOinSBiblio { return $coins_value; } + +=head2 GetMarcPrice + +return the prices in accordance with the Marc format. +=cut + +sub GetMarcPrice { + my ( $record, $marcflavour ) = @_; + my @listtags; + my $subfield; + + if ( $marcflavour eq "MARC21" ) { + @listtags = ('345', '020'); + $subfield="c"; + } elsif ( $marcflavour eq "UNIMARC" ) { + @listtags = ('345', '010'); + $subfield="d"; + } else { + return; + } + + for my $field ( $record->field(@listtags) ) { + for my $subfield_value ($field->subfield($subfield)){ + #check value + return $subfield_value if ($subfield_value); + } + } + return 0; # no price found +} + +=head2 GetMarcQuantity + +return the quantity of a book. Used in acquisition only, when importing a file an iso2709 from a bookseller +Warning : this is not really in the marc standard. In Unimarc, Electre (the most widely used bookseller) use the 969$a + +=cut + +sub GetMarcQuantity { + my ( $record, $marcflavour ) = @_; + my @listtags; + my $subfield; + + if ( $marcflavour eq "MARC21" ) { + return 0 + } elsif ( $marcflavour eq "UNIMARC" ) { + @listtags = ('969'); + $subfield="a"; + } else { + return; + } + + for my $field ( $record->field(@listtags) ) { + for my $subfield_value ($field->subfield($subfield)){ + #check value + if ($subfield_value) { + # in France, the cents separator is the , but sometimes, ppl use a . + # in this case, the price will be x100 when unformatted ! Replace the . by a , to get a proper price calculation + $subfield_value =~ s/\./,/ if C4::Context->preference("CurrencyFormat") eq "FR"; + return $subfield_value; + } + } + } + return 0; # no price found +} + + =head2 GetAuthorisedValueDesc my $subfieldvalue =get_authorised_value_desc( diff --git a/acqui/addorderiso2709.pl b/acqui/addorderiso2709.pl index 29f8143576..c46263a243 100755 --- a/acqui/addorderiso2709.pl +++ b/acqui/addorderiso2709.pl @@ -3,9 +3,8 @@ #A script that lets the user populate a basket from an iso2709 file #the script first displays a list of import batches, then when a batch is selected displays all the biblios in it. #The user can then pick which biblios he wants to order -#written by john.soros@biblibre.com 01/12/2008 -# Copyright 2008 - 2009 BibLibre SARL +# Copyright 2008 - 2011 BibLibre SARL # # This file is part of Koha. # @@ -25,20 +24,26 @@ use strict; use warnings; use CGI; +use Number::Format qw(:all); + use C4::Context; use C4::Auth; use C4::Input; use C4::Output; -use C4::ImportBatch qw/GetImportBatchRangeDesc GetNumberOfNonZ3950ImportBatches GetImportRecordMatches GetImportBibliosRange GetImportBatchOverlayAction GetImportBatchNoMatchAction GetImportBatchItemAction GetImportRecordMarc GetImportBatch/; +use C4::ImportBatch; use C4::Matcher; use C4::Search qw/FindDuplicate BiblioAddAuthorities/; -use C4::Acquisition qw/NewOrder/; +use C4::Acquisition; use C4::Biblio; use C4::Items; -use C4::Koha qw/GetItemTypes/; -use C4::Budgets qw/GetBudgets/; -use C4::Acquisition qw/NewOrderItem GetBasket/; +use C4::Koha; +use C4::Budgets; +use C4::Acquisition; use C4::Bookseller qw/GetBookSellerFromId/; +use C4::Dates; +use C4::Suggestions; # GetSuggestion +use C4::Branch; # GetBranches +use C4::Members; my $input = new CGI; my ($template, $loggedinuser, $cookie) = get_template_and_user({ @@ -53,6 +58,7 @@ my $cgiparams = $input->Vars; my $op = $cgiparams->{'op'}; my $booksellerid = $input->param('booksellerid'); my $bookseller = GetBookSellerFromId($booksellerid); +my $data; $template->param(scriptname => "/cgi-bin/koha/acqui/addorderiso2709.pl", booksellerid => $booksellerid, @@ -69,17 +75,73 @@ if (! $cgiparams->{'basketno'}){ die "Basketnumber required to order from iso2709 file import"; } +# +# 1st step = choose the file to import into acquisition +# if ($op eq ""){ $template->param("basketno" => $cgiparams->{'basketno'}); #display batches import_batches_list($template); +# +# 2nd step = display the content of the choosen file +# } elsif ($op eq "batch_details"){ #display lines inside the selected batch + # get currencies (for change rates calcs if needed) + my $active_currency = GetCurrency(); + my $default_currency; + if (! $data->{currency} ) { # New order no currency set + if ( $bookseller->{listprice} ) { + $default_currency = $bookseller->{listprice}; + } + else { + $default_currency = $active_currency->{currency}; + } + } + my @rates = GetCurrencies(); + + # ## @rates + + my @loop_currency = (); + for my $curr ( @rates ) { + my $selected; + if ($data->{currency} ) { + $selected = $curr->{currency} eq $data->{currency}; + } + else { + $selected = $curr->{currency} eq $default_currency; + } + push @loop_currency, { + currcode => $curr->{currency}, + rate => $curr->{rate}, + selected => $selected, + } + } + $template->param("batch_details" => 1, - "basketno" => $cgiparams->{'basketno'}); + "basketno" => $cgiparams->{'basketno'}, + loop_currencies => \@loop_currency, + ); import_biblios_list($template, $cgiparams->{'import_batch_id'}); - -} elsif ($op eq 'import_records'){ + if ( C4::Context->preference('AcqCreateItem') eq 'ordering' && !$ordernumber ) { + # prepare empty item form + my $cell = PrepareItemrecordDisplay( '', '', '', 'ACQ' ); + + # warn "==> ".Data::Dumper::Dumper($cell); + unless ($cell) { + $cell = PrepareItemrecordDisplay( '', '', '', '' ); + $template->param( 'NoACQframework' => 1 ); + } + my @itemloop; + push @itemloop, $cell; + + $template->param( items => \@itemloop ); + } +# +# 3rd step = import the records +# +} elsif ( $op eq 'import_records' ) { + my $num=FormatNumber(); #import selected lines $template->param('basketno' => $cgiparams->{'basketno'}); # Budget_id is mandatory for adding an order, we just add a default, the user needs to modify this aftewards @@ -95,141 +157,97 @@ if ($op eq ""){ my $import_batch_id = $cgiparams->{'import_batch_id'}; my $biblios = GetImportBibliosRange($import_batch_id); for my $biblio (@$biblios){ - if($cgiparams->{'order-'.$biblio->{'import_record_id'}}){ - my ($marcblob, $encoding) = GetImportRecordMarc($biblio->{'import_record_id'}); - my $marcrecord = MARC::Record->new_from_usmarc($marcblob) || die "couldn't translate marc information"; - my ($duplicatetitle, $biblionumber); - if(!(($biblionumber,$duplicatetitle) = FindDuplicate($marcrecord))){ -#FIXME: missing: marc21 support (should be same with different field) - if ( C4::Context->preference("marcflavour") eq 'UNIMARC' ) { - my $itemtypeid = "itemtype-" . $biblio->{'import_record_id'}; - $marcrecord->field(200)->update("b" => $cgiparams->{$itemtypeid}); - } - # add the biblio - my $bibitemnum; - # remove ISBN - - my ($isbnfield,$isbnsubfield) = GetMarcFromKohaField('biblioitems.isbn',''); - if ( $marcrecord->field($isbnfield) ) { - foreach my $field ( $marcrecord->field($isbnfield) ) { - foreach my $subfield ( $field->subfield($isbnsubfield) ) { - my $newisbn = $field->subfield($isbnsubfield); - $newisbn =~ s/-//g; - $field->update( $isbnsubfield => $newisbn ); - } + # 1st insert the biblio, or find it through matcher + my ( $marcblob, $encoding ) = GetImportRecordMarc( $biblio->{'import_record_id'} ); + my $marcrecord = MARC::Record->new_from_usmarc($marcblob) || die "couldn't translate marc information"; + my $match = GetImportRecordMatches( $biblio->{'import_record_id'}, 1 ); + my $biblionumber=$#$match > -1?$match->[0]->{'biblionumber'}:0; + + unless ( $biblionumber ) { + # add the biblio + my $bibitemnum; + + # remove ISBN - + my ( $isbnfield, $isbnsubfield ) = GetMarcFromKohaField( 'biblioitems.isbn', '' ); + if ( $marcrecord->field($isbnfield) ) { + foreach my $field ( $marcrecord->field($isbnfield) ) { + foreach my $subfield ( $field->subfield($isbnsubfield) ) { + my $newisbn = $field->subfield($isbnsubfield); + $newisbn =~ s/-//g; + $field->update( $isbnsubfield => $newisbn ); } } - - ( $biblionumber, $bibitemnum ) = AddBiblio( $marcrecord, $cgiparams->{'frameworkcode'} || '' ); - } else { - warn("Duplicate item found: ", $biblionumber, "; Duplicate: ", $duplicatetitle); } + ( $biblionumber, $bibitemnum ) = AddBiblio( $marcrecord, $cgiparams->{'frameworkcode'} || '' ); + # 2nd add authorities if applicable if (C4::Context->preference("BiblioAddsAuthorities")){ my ($countlinked,$countcreated)=BiblioAddAuthorities($marcrecord, $cgiparams->{'frameworkcode'}); } - my $patron = C4::Members->GetMember(borrowernumber => $loggedinuser); - my $branch = C4::Branch->GetBranchDetail($patron->{branchcode}); - my ($invoice); - my %orderinfo = ("biblionumber", $biblionumber, - "basketno", $cgiparams->{'basketno'}, - "quantity", $cgiparams->{'quantityrec-' . $biblio->{'import_record_id'}}, - "branchcode", $branch, - "booksellerinvoicenumber", $invoice, - "budget_id", $budget_id, - "uncertainprice", 1, - ); - # get the price if there is one. - # filter by storing only the 1st number - # we suppose the currency is correct, as we have no possibilities to get it. - if ($marcrecord->subfield("345","d")) { - $orderinfo{'listprice'} = $marcrecord->subfield("345","d"); - if ($orderinfo{'listprice'} =~ /^([\d\.,]*)/) { - $orderinfo{'listprice'} = $1; - $orderinfo{'listprice'} =~ s/,/\./; - my $basket = GetBasket($orderinfo{basketno}); - my $bookseller = GetBookSellerFromId($basket->{booksellerid}); - # '//' is like '||' but tests for defined, rather than true - my $gst = $bookseller->{gstrate} // C4::Context->preference("gist") // 0; - $orderinfo{'unitprice'} = $orderinfo{listprice} - ($orderinfo{listprice} * ($bookseller->{discount} / 100)); - $orderinfo{'ecost'} = $orderinfo{unitprice}; - } else { - $orderinfo{'listprice'} = 0; - } - $orderinfo{'rrp'} = $orderinfo{'listprice'}; - } - elsif ($marcrecord->subfield("010","d")) { - $orderinfo{'listprice'} = $marcrecord->subfield("010","d"); - if ($orderinfo{'listprice'} =~ /^([\d\.,]*)/) { - $orderinfo{'listprice'} = $1; - $orderinfo{'listprice'} =~ s/,/\./; - my $basket = GetBasket($orderinfo{basketno}); - my $bookseller = GetBookSellerFromId($basket->{booksellerid}); - my $gst = $bookseller->{gstrate} // C4::Context->preference("gist") // 0; - $orderinfo{'unitprice'} = $orderinfo{listprice} - ($orderinfo{listprice} * ($bookseller->{discount} / 100)); - $orderinfo{'ecost'} = $orderinfo{unitprice}; - } else { - $orderinfo{'listprice'} = 0; - } - $orderinfo{'rrp'} = $orderinfo{'listprice'}; - } - # remove uncertainprice flag if we have found a price in the MARC record - $orderinfo{uncertainprice} = 0 if $orderinfo{listprice}; - my $basketno; - ( $basketno, $ordernumber ) = NewOrder(\%orderinfo); - - # now, add items if applicable - # parse all items sent by the form, and create an item just for the import_record_id we are dealing with - # this is not optimised, but it's working ! - if (C4::Context->preference('AcqCreateItem') eq 'ordering') { - my @tags = $input->param('tag'); - my @subfields = $input->param('subfield'); - my @field_values = $input->param('field_value'); - my @serials = $input->param('serial'); - my @itemids = $input->param('itemid'); # hint : in iso2709, the itemid contains the import_record_id, not an item id. It is used to get the right item, as we have X biblios. - my @ind_tag = $input->param('ind_tag'); - my @indicator = $input->param('indicator'); - #Rebuilding ALL the data for items into a hash - # parting them on $itemid. - my %itemhash; - my $range=scalar(@itemids); - - my $i = 0; - my @items; - for my $itemid (@itemids){ - my $realitemid; #javascript generated random itemids, in the form itemid-randomnumber, $realitemid is the itemid, while $itemid is the itemide parsed from the html - if ($itemid =~ m/(\d+)-.*/){ - my @splits = split(/-/, $itemid); - $realitemid = $splits[0]; - } - if ( ( $realitemid && $cgiparams->{'order-'. $realitemid} && $realitemid eq $biblio->{import_record_id}) || ($itemid && $cgiparams->{'order-'. $itemid} && $itemid eq $biblio->{import_record_id}) ){ - my ($item, $found); - for my $tmpitem (@items){ - if ($tmpitem->{itemid} eq $itemid){ - $item = $tmpitem; - $found = 1; - } - } - push @{$item->{tags}}, $tags[$i]; - push @{$item->{subfields}}, $subfields[$i]; - push @{$item->{field_values}}, $field_values[$i]; - push @{$item->{ind_tag}}, $ind_tag[$i]; - push @{$item->{indicator}}, $indicator[$i]; - $item->{itemid} = $itemid; - if (! $found){ - push @items, $item; - } - } - ++$i - } - foreach my $item (@items){ - my $xml = TransformHtmlToXml( $item->{'tags'}, - $item->{'subfields'}, - $item->{'field_values'}, - $item->{'ind_tag'}, - $item->{'indicator'}); - my $record=MARC::Record::new_from_xml($xml, 'UTF-8'); - my ($biblionumber,$bibitemnum,$itemnumber) = AddItemFromMarc($record,$biblionumber); - NewOrderItem( $itemnumber, $ordernumber); - } + } else { + SetImportRecordStatus( $biblio->{'import_record_id'}, 'imported' ); + } + # 3rd add order + my $patron = C4::Members->GetMember( borrowernumber => $loggedinuser ); + my $branch = C4::Branch->GetBranchDetail( $patron->{branchcode} ); + my ($invoice); + # get quantity in the MARC record (1 if none) + my $quantity = GetMarcQuantity($marcrecord, C4::Context->preference('marcflavour')) || 1; + my %orderinfo = ( + "biblionumber", $biblionumber, "basketno", $cgiparams->{'basketno'}, + "quantity", $quantity, "branchcode", $branch, + "booksellerinvoicenumber", $invoice, + "budget_id", $budget_id, "uncertainprice", 1, + "sort1", $cgiparams->{'sort1'},"sort2", $cgiparams->{'sort2'}, + "notes", $cgiparams->{'notes'}, "budget_id", $cgiparams->{'budget_id'}, + "currency",$cgiparams->{'currency'}, + ); + # get the price if there is one. + # filter by storing only the 1st number + # we suppose the currency is correct, as we have no possibilities to get it. + my $price= GetMarcPrice($marcrecord, C4::Context->preference('marcflavour')); + if ($price){ + $price = $num->unformat_number($price); + } + if ($price){ + $orderinfo{'listprice'} = $price; + eval "use C4::Acquisition qw/GetBasket/;"; + eval "use C4::Bookseller qw/GetBookSellerFromId/;"; + my $basket = GetBasket( $orderinfo{basketno} ); + my $bookseller = GetBookSellerFromId( $basket->{booksellerid} ); + my $gst = $bookseller->{gstrate} || C4::Context->preference("gist") || 0; + $orderinfo{'unitprice'} = $orderinfo{listprice} - ( $orderinfo{listprice} * ( $bookseller->{discount} / 100 ) ); + $orderinfo{'ecost'} = $orderinfo{unitprice}; + } else { + $orderinfo{'listprice'} = 0; + } + $orderinfo{'rrp'} = $orderinfo{'listprice'}; + + # remove uncertainprice flag if we have found a price in the MARC record + $orderinfo{uncertainprice} = 0 if $orderinfo{listprice}; + my $basketno; + ( $basketno, $ordernumber ) = NewOrder( \%orderinfo ); + + # 4th, add items if applicable + # parse the item sent by the form, and create an item just for the import_record_id we are dealing with + # this is not optimised, but it's working ! + if ( C4::Context->preference('AcqCreateItem') eq 'ordering' ) { + my @tags = $input->param('tag'); + my @subfields = $input->param('subfield'); + my @field_values = $input->param('field_value'); + my @serials = $input->param('serial'); + my @ind_tag = $input->param('ind_tag'); + my @indicator = $input->param('indicator'); + my $item; + push @{ $item->{tags} }, $tags[0]; + push @{ $item->{subfields} }, $subfields[0]; + push @{ $item->{field_values} }, $field_values[0]; + push @{ $item->{ind_tag} }, $ind_tag[0]; + push @{ $item->{indicator} }, $indicator[0]; + my $xml = TransformHtmlToXml( \@tags, \@subfields, \@field_values, \@ind_tag, \@indicator ); + my $record = MARC::Record::new_from_xml( $xml, 'UTF-8' ); + for (my $qtyloop=1;$qtyloop <=$quantity;$qtyloop++) { + my ( $biblionumber, $bibitemnum, $itemnumber ) = AddItemFromMarc( $record, $biblionumber ); + NewOrderItem( $itemnumber, $ordernumber ); } } } @@ -237,6 +255,65 @@ if ($op eq ""){ print $input->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=".$cgiparams->{'basketno'}); exit; } + +my $budgets = GetBudgets(); +my $budget_id = @$budgets[0]->{'budget_id'}; +# build bookfund list +my $borrower = GetMember( 'borrowernumber' => $loggedinuser ); +my ( $flags, $homebranch ) = ( $borrower->{'flags'}, $borrower->{'branchcode'} ); +my $budget = GetBudget($budget_id); + +# build budget list +my $budget_loop = []; +my $budgets = GetBudgetHierarchy( q{}, $borrower->{branchcode}, $borrower->{borrowernumber} ); +foreach my $r ( @{$budgets} ) { + if ( !defined $r->{budget_amount} || $r->{budget_amount} == 0 ) { + next; + } + push @{$budget_loop}, + { b_id => $r->{budget_id}, + b_txt => $r->{budget_name}, + b_sel => ( $r->{budget_id} == $budget_id ) ? 1 : 0, + }; +} +$template->param( budget_loop => $budget_loop,); + +my $CGIsort1; +if ($budget) { # its a mod .. + if ( defined $budget->{'sort1_authcat'} ) { # with custom Asort* planning values + $CGIsort1 = GetAuthvalueDropbox( 'sort1', $budget->{'sort1_authcat'}, $data->{'sort1'} ); + } +} elsif ( scalar(@$budgets) ) { + $CGIsort1 = GetAuthvalueDropbox( 'sort1', @$budgets[0]->{'sort1_authcat'}, '' ); +} else { + $CGIsort1 = GetAuthvalueDropbox( 'sort1', '', '' ); +} + +# if CGIsort is successfully fetched, the use it +# else - failback to plain input-field +if ($CGIsort1) { + $template->param( CGIsort1 => $CGIsort1 ); +} else { + $template->param( sort1 => $data->{'sort1'} ); +} + +my $CGIsort2; +if ($budget) { + if ( defined $budget->{'sort2_authcat'} ) { + $CGIsort2 = GetAuthvalueDropbox( 'sort2', $budget->{'sort2_authcat'}, $data->{'sort2'} ); + } +} elsif ( scalar(@$budgets) ) { + $CGIsort2 = GetAuthvalueDropbox( 'sort2', @$budgets[0]->{sort2_authcat}, '' ); +} else { + $CGIsort2 = GetAuthvalueDropbox( 'sort2', '', '' ); +} + +if ($CGIsort2) { + $template->param( CGIsort2 => $CGIsort2 ); +} else { + $template->param( sort2 => $data->{'sort2'} ); +} + output_html_with_http_headers $input, $cookie, $template->output; @@ -268,12 +345,7 @@ sub import_biblios_list { my $batch = GetImportBatch($import_batch_id,'staged'); my $biblios = GetImportBibliosRange($import_batch_id,'','','staged'); my @list = (); -# # Itemtype is mandatory for adding a biblioitem, we just add a default, the user needs to modify this aftewards -# my $itemtypehash = GetItemTypes(); -# my @itemtypes; -# for my $key (sort { $itemtypehash->{$a}->{description} cmp $itemtypehash->{$b}->{description} } keys %$itemtypehash) { -# push(@itemtypes, $itemtypehash->{$key}); -# } + foreach my $biblio (@$biblios) { my $citation = $biblio->{'title'}; $citation .= " $biblio->{'author'}" if $biblio->{'author'}; @@ -293,18 +365,8 @@ sub import_biblios_list { match_biblionumber => $#$match > -1 ? $match->[0]->{'biblionumber'} : 0, match_citation => $#$match > -1 ? $match->[0]->{'title'} . ' ' . $match->[0]->{'author'} : '', match_score => $#$match > -1 ? $match->[0]->{'score'} : 0, -# itemtypes => \@itemtypes, ); -# if (C4::Context->preference('AcqCreateItem') eq 'ordering' && !$ordernumber) { -# # prepare empty item form -# my $cell = PrepareItemrecordDisplay(); -# my @itemloop; -# push @itemloop,$cell; -# $cellrecord{'items'} = \@itemloop; -# } push @list, \%cellrecord; - - } my $num_biblios = $batch->{'num_biblios'}; my $overlay_action = GetImportBatchOverlayAction($import_batch_id); diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/addorderiso2709.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/addorderiso2709.tmpl index 94637d1332..1bd65e4367 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/addorderiso2709.tmpl +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/addorderiso2709.tmpl @@ -8,6 +8,7 @@ +