3 # Copyright 2014,2015 PTFS-Europe Ltd
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 use base
qw(Exporter);
25 use English
qw{ -no_match_vars
};
31 use C4
::Acquisition
qw( NewBasket ModOrder);
32 use C4
::Suggestions
qw( ModSuggestion );
33 use C4
::Biblio
qw( AddBiblio TransformKohaToMarc GetMarcBiblio GetFrameworkCode GetMarcFromKohaField );
34 use Koha
::Edifact
::Order
;
38 use Koha
::Plugins
::Handler
;
39 use Koha
::Acquisition
::Baskets
;
40 use Koha
::Acquisition
::Booksellers
;
44 qw( process_quote process_invoice process_ordrsp create_edi_order get_edifact_ean );
46 sub create_edi_order
{
47 my $parameters = shift;
48 my $basketno = $parameters->{basketno
};
49 my $ean = $parameters->{ean
};
50 my $branchcode = $parameters->{branchcode
};
51 my $noingest = $parameters->{noingest
};
52 if ( !$basketno || !$ean ) {
53 carp
'create_edi_order called with no basketno or ean';
57 my $schema = Koha
::Database
->new()->schema();
59 my @orderlines = $schema->resultset('Aqorder')->search(
61 basketno
=> $basketno,
67 carp
"No orderlines for basket $basketno";
71 my $vendor = $schema->resultset('VendorEdiAccount')->search(
73 vendor_id
=> $orderlines[0]->basketno->booksellerid->id,
77 my $ean_search_keys = { ean
=> $ean, };
79 $ean_search_keys->{branchcode
} = $branchcode;
82 $schema->resultset('EdifactEan')->search($ean_search_keys)->single;
84 # If no branch specific each can be found, look for a default ean
86 $ean_obj = $schema->resultset('EdifactEan')->search(
94 my $dbh = C4
::Context
->dbh;
95 my $arr_ref = $dbh->selectcol_arrayref(
96 'select id from edifact_messages where basketno = ? and message_type = \'QUOTE\'',
99 my $response = @
{$arr_ref} ?
1 : 0;
101 my $edifact_order_params = {
102 orderlines
=> \
@orderlines,
105 is_response
=> $response,
109 if ( $vendor->plugin ) {
110 $edifact = Koha
::Plugins
::Handler
->run(
112 class => $vendor->plugin,
113 method
=> 'edifact_order',
115 params
=> $edifact_order_params,
121 $edifact = Koha
::Edifact
::Order
->new($edifact_order_params);
124 return unless $edifact;
126 my $order_file = $edifact->encode();
130 my $m = unidecode
($order_file); # remove diacritics and non-latin chars
131 if ($noingest) { # allows scripts to produce test files
135 message_type
=> 'ORDERS',
137 vendor_id
=> $vendor->vendor_id,
139 basketno
=> $basketno,
140 filename
=> $edifact->filename(),
141 transfer_date
=> $edifact->msg_date_string(),
142 edi_acct
=> $vendor->id,
145 $schema->resultset('EdifactMessage')->create($order);
153 my $response_message = shift;
154 $response_message->status('processing');
155 $response_message->update;
156 my $schema = Koha
::Database
->new()->schema();
157 my $logger = Log
::Log4perl
->get_logger();
160 Koha
::Edifact
->new( { transmission
=> $response_message->raw_msg, } );
161 my $messages = $edi->message_array();
163 if ( @
{$messages} ) {
164 foreach my $msg ( @
{$messages} ) {
165 my $lines = $msg->lineitems();
166 foreach my $line ( @
{$lines} ) {
167 my $ordernumber = $line->ordernumber();
169 # action cancelled:change_requested:no_action:accepted:not_found:recorded
170 my $action = $line->action_notification();
171 if ( $action eq 'cancelled' ) {
172 my $reason = $line->coded_orderline_text();
175 ordernumber
=> $ordernumber,
176 cancellationreason
=> $reason,
177 orderstatus
=> 'cancelled',
178 datecancellationprinted
=> dt_from_string
()->ymd(),
182 else { # record order as due with possible further info
184 my $report = $line->coded_orderline_text();
185 my $date_avail = $line->availability_date();
188 $report .= " Available: $date_avail";
192 ordernumber
=> $ordernumber,
193 suppliers_report
=> $report,
201 $response_message->status('received');
202 $response_message->update;
206 sub process_invoice
{
207 my $invoice_message = shift;
208 $invoice_message->status('processing');
209 $invoice_message->update;
210 my $schema = Koha
::Database
->new()->schema();
211 my $logger = Log
::Log4perl
->get_logger();
214 my $plugin_class = $invoice_message->edi_acct()->plugin();
216 # Plugin has its own invoice processor, only run it and not the standard invoice processor below
217 if ( $plugin_class ) {
218 my $plugin = $plugin_class->new();
219 if ( $plugin->can('edifact_process_invoice') ) {
220 Koha
::Plugins
::Handler
->run(
222 class => $plugin_class,
223 method
=> 'edifact_process_invoice',
225 invoice
=> $invoice_message,
234 if ( $plugin_class ) {
235 $edi_plugin = Koha
::Plugins
::Handler
->run(
237 class => $plugin_class,
240 invoice_message
=> $invoice_message,
241 transmission
=> $invoice_message->raw_msg,
247 my $edi = $edi_plugin ||
248 Koha
::Edifact
->new( { transmission
=> $invoice_message->raw_msg, } );
250 my $messages = $edi->message_array();
252 if ( @
{$messages} ) {
254 # BGM contains an invoice number
255 foreach my $msg ( @
{$messages} ) {
256 my $invoicenumber = $msg->docmsg_number();
257 my $shipmentcharge = $msg->shipment_charge();
258 my $msg_date = $msg->message_date;
259 my $tax_date = $msg->tax_point_date;
260 if ( !defined $tax_date || $tax_date !~ m/^\d{8}/xms ) {
261 $tax_date = $msg_date;
264 my $vendor_ean = $msg->supplier_ean;
265 if ( !defined $vendor_acct || $vendor_ean ne $vendor_acct->san ) {
266 $vendor_acct = $schema->resultset('VendorEdiAccount')->search(
272 if ( !$vendor_acct ) {
274 "Cannot find vendor with ean $vendor_ean for invoice $invoicenumber in $invoice_message->filename";
277 $invoice_message->edi_acct( $vendor_acct->id );
278 $logger->trace("Adding invoice:$invoicenumber");
279 my $new_invoice = $schema->resultset('Aqinvoice')->create(
281 invoicenumber
=> $invoicenumber,
282 booksellerid
=> $invoice_message->vendor_id,
283 shipmentdate
=> $msg_date,
284 billingdate
=> $tax_date,
285 shipmentcost
=> $shipmentcharge,
286 shipmentcost_budgetid
=> $vendor_acct->shipment_budget,
287 message_id
=> $invoice_message->id,
290 my $invoiceid = $new_invoice->invoiceid;
291 $logger->trace("Added as invoiceno :$invoiceid");
292 my $lines = $msg->lineitems();
294 foreach my $line ( @
{$lines} ) {
295 my $ordernumber = $line->ordernumber;
296 $logger->trace( "Receipting order:$ordernumber Qty: ",
299 my $order = $schema->resultset('Aqorder')->find($ordernumber);
301 # ModReceiveOrder does not validate that $ordernumber exists validate here
305 my $s = $schema->resultset('Suggestion')->search(
307 biblionumber
=> $order->biblionumber->biblionumber,
313 suggestionid
=> $s->suggestionid,
314 STATUS
=> 'AVAILABLE',
319 my $price = _get_invoiced_price
($line);
321 if ( $order->quantity > $line->quantity ) {
322 my $ordered = $order->quantity;
325 $order->orderstatus('partial');
326 $order->quantity( $ordered - $line->quantity );
328 my $received_order = $order->copy(
330 ordernumber
=> undef,
331 quantity
=> $line->quantity,
332 quantityreceived
=> $line->quantity,
333 orderstatus
=> 'complete',
335 invoiceid
=> $invoiceid,
336 datereceived
=> $msg_date,
339 transfer_items
( $schema, $line, $order,
341 receipt_items
( $schema, $line,
342 $received_order->ordernumber );
344 else { # simple receipt all copies on order
345 $order->quantityreceived( $line->quantity );
346 $order->datereceived($msg_date);
347 $order->invoiceid($invoiceid);
348 $order->unitprice($price);
349 $order->orderstatus('complete');
351 receipt_items
( $schema, $line, $ordernumber );
356 "No order found for $ordernumber Invoice:$invoicenumber"
366 $invoice_message->status('received');
367 $invoice_message->update; # status and basketno link
371 sub _get_invoiced_price
{
373 my $price = $line->price_net;
374 if ( !defined $price ) { # no net price so generate it from lineitem amount
375 $price = $line->amt_lineitem;
376 if ( $price and $line->quantity > 1 ) {
377 $price /= $line->quantity; # div line cost by qty
384 my ( $schema, $inv_line, $ordernumber ) = @_;
385 my $logger = Log
::Log4perl
->get_logger();
386 my $quantity = $inv_line->quantity;
388 # itemnumber is not a foreign key ??? makes this a bit cumbersome
389 my @item_links = $schema->resultset('AqordersItem')->search(
391 ordernumber
=> $ordernumber,
395 foreach my $ilink (@item_links) {
396 my $item = $schema->resultset('Item')->find( $ilink->itemnumber );
398 my $i = $ilink->itemnumber;
400 "Cannot find aqorder item for $i :Order:$ordernumber");
403 my $b = $item->homebranch->branchcode;
404 if ( !exists $branch_map{$b} ) {
405 $branch_map{$b} = [];
407 push @
{ $branch_map{$b} }, $item;
410 # Handling for 'AcqItemSetSubfieldsWhenReceived'
414 if ( C4
::Context
->preference('AcqCreateItem') eq 'ordering' ) {
415 @affects = split q{\|},
416 C4
::Context
->preference("AcqItemSetSubfieldsWhenReceived");
418 my $order = Koha
::Acquisition
::Orders
->find($ordernumber);
419 $biblionumber = $order->biblionumber;
420 my $frameworkcode = GetFrameworkCode
($biblionumber);
421 ($itemfield) = GetMarcFromKohaField
( 'items.itemnumber',
426 my $gir_occurrence = 0;
427 while ( $gir_occurrence < $quantity ) {
428 my $branch = $inv_line->girfield( 'branch', $gir_occurrence );
429 my $item = shift @
{ $branch_map{$branch} };
431 my $barcode = $inv_line->girfield( 'barcode', $gir_occurrence );
432 if ( $barcode && !$item->barcode ) {
433 my $rs = $schema->resultset('Item')->search(
438 if ( $rs->count > 0 ) {
439 $logger->warn("Barcode $barcode is a duplicate");
443 $logger->trace("Adding barcode $barcode");
444 $item->barcode($barcode);
448 # Handling for 'AcqItemSetSubfieldsWhenReceived'
450 my $item_marc = C4
::Items
::GetMarcItem
( $biblionumber, $item->itemnumber );
451 for my $affect (@affects) {
452 my ( $sf, $v ) = split q{=}, $affect, 2;
453 foreach ( $item_marc->field($itemfield) ) {
454 $_->update( $sf => $v );
457 C4
::Items
::ModItemFromMarc
( $item_marc, $biblionumber, $item->itemnumber );
463 $logger->warn("Unmatched item at branch:$branch");
472 my ( $schema, $inv_line, $order_from, $order_to ) = @_;
474 # Transfer x items from the orig order to a completed partial order
475 my $quantity = $inv_line->quantity;
477 my %mapped_by_branch;
478 while ( $gocc < $quantity ) {
479 my $branch = $inv_line->girfield( 'branch', $gocc );
480 if ( !exists $mapped_by_branch{$branch} ) {
481 $mapped_by_branch{$branch} = 1;
484 $mapped_by_branch{$branch}++;
488 my $logger = Log
::Log4perl
->get_logger();
489 my $o1 = $order_from->ordernumber;
490 my $o2 = $order_to->ordernumber;
491 $logger->warn("transferring $quantity copies from order $o1 to order $o2");
493 my @item_links = $schema->resultset('AqordersItem')->search(
495 ordernumber
=> $order_from->ordernumber,
498 foreach my $ilink (@item_links) {
499 my $ino = $ilink->itemnumber;
500 my $item = $schema->resultset('Item')->find( $ilink->itemnumber );
501 my $i_branch = $item->homebranch;
502 if ( exists $mapped_by_branch{$i_branch}
503 && $mapped_by_branch{$i_branch} > 0 )
505 $ilink->ordernumber( $order_to->ordernumber );
508 --$mapped_by_branch{$i_branch};
509 $logger->warn("Transferred item $item");
512 $logger->warn("Skipped item $item");
514 if ( $quantity < 1 ) {
525 $quote->status('processing');
528 my $edi = Koha
::Edifact
->new( { transmission
=> $quote->raw_msg, } );
530 my $messages = $edi->message_array();
531 my $process_errors = 0;
532 my $logger = Log
::Log4perl
->get_logger();
533 my $schema = Koha
::Database
->new()->schema();
534 my $message_count = 0;
535 my @added_baskets; # if auto & multiple baskets need to order all
537 if ( @
{$messages} && $quote->vendor_id ) {
538 foreach my $msg ( @
{$messages} ) {
541 NewBasket
( $quote->vendor_id, 0, $quote->filename, q{},
543 push @added_baskets, $basketno;
544 if ( $message_count > 1 ) {
545 my $m_filename = $quote->filename;
546 $m_filename .= "_$message_count";
547 $schema->resultset('EdifactMessage')->create(
549 message_type
=> $quote->message_type,
550 transfer_date
=> $quote->transfer_date,
551 vendor_id
=> $quote->vendor_id,
552 edi_acct
=> $quote->edi_acct,
554 basketno
=> $basketno,
556 filename
=> $m_filename,
561 $quote->basketno($basketno);
563 $logger->trace("Created basket :$basketno");
564 my $items = $msg->lineitems();
565 my $refnum = $msg->message_refno;
567 for my $item ( @
{$items} ) {
568 if ( !quote_item
( $item, $quote, $basketno ) ) {
574 my $status = 'received';
575 if ($process_errors) {
579 $quote->status($status);
580 $quote->update; # status and basketno link
581 # Do we automatically generate orders for this vendor
582 my $v = $schema->resultset('VendorEdiAccount')->search(
584 vendor_id
=> $quote->vendor_id,
587 if ( $v->auto_orders ) {
588 for my $b (@added_baskets) {
591 ean
=> $messages->[0]->buyer_ean,
595 Koha
::Acquisition
::Baskets
->find($b)->close;
603 my ( $item, $quote, $basketno ) = @_;
605 my $schema = Koha
::Database
->new()->schema();
606 my $logger = Log
::Log4perl
->get_logger();
608 # $basketno is the return from AddBasket in the calling routine
609 # So this call should not fail unless that has
610 my $basket = Koha
::Acquisition
::Baskets
->find( $basketno );
612 $logger->error('Skipping order creation no valid basketno');
615 $logger->trace( 'Checking db for matches with ', $item->item_number_id() );
616 my $bib = _check_for_existing_bib
( $item->item_number_id() );
617 if ( !defined $bib ) {
619 my $bib_record = _create_bib_from_quote
( $item, $quote );
620 ( $bib->{biblionumber
}, $bib->{biblioitemnumber
} ) =
621 AddBiblio
( $bib_record, q{} );
622 $logger->trace("New biblio added $bib->{biblionumber}");
625 $logger->trace("Match found: $bib->{biblionumber}");
628 # Create an orderline
629 my $order_note = $item->{orderline_free_text
};
631 my $order_quantity = $item->quantity();
632 my $gir_count = $item->number_of_girs();
633 $order_quantity ||= 1; # quantity not necessarily present
634 if ( $gir_count > 1 ) {
635 if ( $gir_count != $order_quantity ) {
637 "Order for $order_quantity items, $gir_count segments present");
639 $order_quantity = 1; # attempts to create an orderline for each gir
641 my $vendor = Koha
::Acquisition
::Booksellers
->find( $quote->vendor_id );
643 # database definitions should set some of these defaults but dont
645 biblionumber
=> $bib->{biblionumber
},
646 entrydate
=> dt_from_string
()->ymd(),
647 basketno
=> $basketno,
648 listprice
=> $item->price,
649 quantity
=> $order_quantity,
650 quantityreceived
=> 0,
651 order_vendornote
=> q{},
652 order_internalnote
=> $order_note,
653 replacementprice
=> $item->price,
654 rrp_tax_included
=> $item->price,
655 rrp_tax_excluded
=> $item->price,
656 ecost
=> _discounted_price
( $quote->vendor->discount, $item->price ),
660 currency
=> $vendor->listprice(),
663 # suppliers references
664 if ( $item->reference() ) {
665 $order_hash->{suppliers_reference_number
} = $item->reference;
666 $order_hash->{suppliers_reference_qualifier
} = 'QLI';
668 elsif ( $item->orderline_reference_number() ) {
669 $order_hash->{suppliers_reference_number
} =
670 $item->orderline_reference_number;
671 $order_hash->{suppliers_reference_qualifier
} = 'SLI';
673 if ( $item->item_number_id ) { # suppliers ean
674 $order_hash->{line_item_id
} = $item->item_number_id;
677 if ( $item->girfield('servicing_instruction') ) {
681 while ( $si = $item->girfield( 'servicing_instruction', $occ ) ) {
688 $order_hash->{order_vendornote
} = $txt;
691 if ( $item->internal_notes() ) {
692 if ( $order_hash->{order_internalnote
} ) { # more than ''
693 $order_hash->{order_internalnote
} .= q{ };
695 $order_hash->{order_internalnote
} .= $item->internal_notes;
698 my $budget = _get_budget
( $schema, $item->girfield('fund_allocation') );
702 if ( $item->quantity > 1 ) {
703 carp
'Skipping line with no budget info';
704 $logger->trace('girfield skipped for invalid budget');
708 carp
'Skipping line with no budget info';
709 $logger->trace('orderline skipped for invalid budget');
719 $order_hash->{budget_id
} = $budget->budget_id;
720 my $first_order = $schema->resultset('Aqorder')->create($order_hash);
721 my $o = $first_order->ordernumber();
722 $logger->trace("Order created :$o");
724 # should be done by database settings
725 $first_order->parent_ordernumber( $first_order->ordernumber() );
726 $first_order->update();
728 # add to $budgets to prevent duplicate orderlines
729 $budgets{ $budget->budget_id } = '1';
731 # record ordernumber against budget
732 $ordernumber{ $budget->budget_id } = $o;
734 if ( C4
::Context
->preference('AcqCreateItem') eq 'ordering' ) {
735 $item_hash = _create_item_from_quote
( $item, $quote );
738 while ( $created < $order_quantity ) {
739 $item_hash->{biblionumber
} = $bib->{biblionumber
};
740 $item_hash->{biblioitemnumber
} = $bib->{biblioitemnumber
};
741 my $kitem = Koha
::Item
->new( $item_hash )->store;
742 my $itemnumber = $kitem->itemnumber;
743 $logger->trace("Added item:$itemnumber");
744 $schema->resultset('AqordersItem')->create(
746 ordernumber
=> $first_order->ordernumber,
747 itemnumber
=> $itemnumber,
755 if ( $order_quantity == 1 && $item->quantity > 1 ) {
756 my $occurrence = 1; # occ zero already added
757 while ( $occurrence < $item->quantity ) {
760 $budget = _get_budget
( $schema,
761 $item->girfield( 'fund_allocation', $occurrence ) );
765 $item->girfield( 'fund_allocation', $occurrence );
766 carp
'Skipping line with no budget info';
768 "girfield skipped for invalid budget:$bad_budget");
769 ++$occurrence; ## lets look at the next one not this one again
773 # add orderline for NEW budget in $budgets
774 if ( !exists $budgets{ $budget->budget_id } ) {
776 # $order_hash->{quantity} = 1; by default above
777 # we should handle both 1:1 GIR & 1:n GIR (with LQT values) here
779 $order_hash->{budget_id
} = $budget->budget_id;
782 $schema->resultset('Aqorder')->create($order_hash);
783 my $o = $new_order->ordernumber();
784 $logger->trace("Order created :$o");
786 # should be done by database settings
787 $new_order->parent_ordernumber( $new_order->ordernumber() );
788 $new_order->update();
790 # add to $budgets to prevent duplicate orderlines
791 $budgets{ $budget->budget_id } = '1';
793 # record ordernumber against budget
794 $ordernumber{ $budget->budget_id } = $o;
796 if ( C4
::Context
->preference('AcqCreateItem') eq 'ordering' ) {
797 if ( !defined $item_hash ) {
798 $item_hash = _create_item_from_quote
( $item, $quote );
802 $item->girfield( 'stock_category', $occurrence ),
804 $item->girfield( 'collection_code', $occurrence ),
806 $item->girfield( 'shelfmark', $occurrence )
807 || $item->girfield( 'classification', $occurrence )
808 || title_level_class
($item),
810 $item->girfield( 'branch', $occurrence ),
811 homebranch
=> $item->girfield( 'branch', $occurrence ),
813 if ( $new_item->{itype
} ) {
814 $item_hash->{itype
} = $new_item->{itype
};
816 if ( $new_item->{location
} ) {
817 $item_hash->{location
} = $new_item->{location
};
819 if ( $new_item->{itemcallnumber
} ) {
820 $item_hash->{itemcallnumber
} =
821 $new_item->{itemcallnumber
};
823 if ( $new_item->{holdingbranch
} ) {
824 $item_hash->{holdingbranch
} =
825 $new_item->{holdingbranch
};
827 if ( $new_item->{homebranch
} ) {
828 $item_hash->{homebranch
} = $new_item->{homebranch
};
831 $item_hash->{biblionumber
} = $bib->{biblionumber
};
832 $item_hash->{biblioitemnumber
} = $bib->{biblioitemnumber
};
833 my $kitem = Koha
::Item
->new( $item_hash )->store;
834 my $itemnumber = $kitem->itemnumber;
835 $logger->trace("New item $itemnumber added");
836 $schema->resultset('AqordersItem')->create(
838 ordernumber
=> $new_order->ordernumber,
839 itemnumber
=> $itemnumber,
844 $item->girfield( 'library_rotation_plan', $occurrence );
847 Koha
::StockRotationRotas
->find( { title
=> $lrp },
848 { key
=> 'stockrotationrotas_title' } );
850 $rota->add_item($itemnumber);
851 $logger->trace("Item added to rota $rota->id");
855 "No rota found matching $lrp in orderline");
863 # increment quantity in orderline for EXISTING budget in $budgets
865 my $row = $schema->resultset('Aqorder')->find(
867 ordernumber
=> $ordernumber{ $budget->budget_id }
871 my $qty = $row->quantity;
880 # Do not use the basket level value as it is always NULL
881 # See calling subs call to AddBasket
882 if ( C4
::Context
->preference('AcqCreateItem') eq 'ordering' ) {
887 price
=> $item->price,
888 replacementprice
=> $item->price,
890 $item->girfield( 'stock_category', $occurrence ),
892 $item->girfield( 'collection_code', $occurrence ),
894 $item->girfield( 'shelfmark', $occurrence )
895 || $item->girfield( 'classification', $occurrence )
896 || $item_hash->{itemcallnumber
},
898 $item->girfield( 'branch', $occurrence ),
899 homebranch
=> $item->girfield( 'branch', $occurrence ),
901 $new_item->{biblionumber
} = $bib->{biblionumber
};
902 $new_item->{biblioitemnumber
} = $bib->{biblioitemnumber
};
903 my $kitem = Koha
::Item
->new( $new_item )->store;
904 my $itemnumber = $kitem->itemnumber;
905 $logger->trace("New item $itemnumber added");
906 $schema->resultset('AqordersItem')->create(
908 ordernumber
=> $ordernumber{ $budget->budget_id },
909 itemnumber
=> $itemnumber,
914 $item->girfield( 'library_rotation_plan', $occurrence );
917 Koha
::StockRotationRotas
->find( { title
=> $lrp },
918 { key
=> 'stockrotationrotas_title' } );
920 $rota->add_item($itemnumber);
921 $logger->trace("Item added to rota $rota->id");
925 "No rota found matching $lrp in orderline");
938 sub get_edifact_ean
{
940 my $dbh = C4
::Context
->dbh;
942 my $eans = $dbh->selectcol_arrayref('select ean from edifact_ean');
947 # We should not need to have a routine to do this here
948 sub _discounted_price
{
949 my ( $discount, $price ) = @_;
950 return $price - ( ( $discount * $price ) / 100 );
953 sub _check_for_existing_bib
{
956 my $search_isbn = $isbn;
957 $search_isbn =~ s/^\s*/%/xms;
958 $search_isbn =~ s/\s*$/%/xms;
959 my $dbh = C4
::Context
->dbh;
960 my $sth = $dbh->prepare(
961 'select biblionumber, biblioitemnumber from biblioitems where isbn like ?',
964 $dbh->selectall_arrayref( $sth, { Slice
=> {} }, $search_isbn );
965 if ( @
{$tuple_arr} ) {
966 return $tuple_arr->[0];
968 elsif ( length($isbn) == 13 && $isbn !~ /^97[89]/ ) {
969 my $tarr = $dbh->selectall_arrayref(
970 'select biblionumber, biblioitemnumber from biblioitems where ean = ?',
981 if ( $isbn =~ m/(\d{13})/xms ) {
982 my $b_isbn = Business
::ISBN
->new($1);
983 if ( $b_isbn && $b_isbn->is_valid && $b_isbn->as_isbn10 ) {
984 $search_isbn = $b_isbn->as_isbn10->as_string( [] );
988 elsif ( $isbn =~ m/(\d{9}[xX]|\d{10})/xms ) {
989 my $b_isbn = Business
::ISBN
->new($1);
990 if ( $b_isbn && $b_isbn->is_valid ) {
991 $search_isbn = $b_isbn->as_isbn13->as_string( [] );
996 $search_isbn = "%$search_isbn%";
998 $dbh->selectall_arrayref( $sth, { Slice
=> {} }, $search_isbn );
999 if ( @
{$tuple_arr} ) {
1000 return $tuple_arr->[0];
1007 # returns a budget obj or undef
1008 # fact we need this shows what a mess Acq API is
1010 my ( $schema, $budget_code ) = @_;
1011 my $period_rs = $schema->resultset('Aqbudgetperiod')->search(
1013 budget_period_active
=> 1,
1017 # db does not ensure budget code is unque
1018 return $schema->resultset('Aqbudget')->single(
1020 budget_code
=> $budget_code,
1022 { -in => $period_rs->get_column('budget_period_id')->as_query },
1027 # try to get title level classification from incoming quote
1028 sub title_level_class
{
1031 my $default_scheme = C4
::Context
->preference('DefaultClassificationSource');
1032 if ( $default_scheme eq 'ddc' ) {
1033 $class = $item->dewey_class();
1035 elsif ( $default_scheme eq 'lcc' ) {
1036 $class = $item->lc_class();
1040 $item->girfield('shelfmark')
1041 || $item->girfield('classification')
1047 sub _create_bib_from_quote
{
1049 #TBD we should flag this for updating from an external source
1050 #As biblio (&biblioitems) has no candidates flag in order
1051 my ( $item, $quote ) = @_;
1052 my $itemid = $item->item_number_id;
1053 my $defalt_classification_source =
1054 C4
::Context
->preference('DefaultClassificationSource');
1056 'biblioitems.cn_source' => $defalt_classification_source,
1057 'items.cn_source' => $defalt_classification_source,
1058 'items.notforloan' => -1,
1059 'items.cn_sort' => q{},
1061 $bib_hash->{'biblio.seriestitle'} = $item->series;
1063 $bib_hash->{'biblioitems.publishercode'} = $item->publisher;
1064 $bib_hash->{'biblioitems.publicationyear'} =
1065 $bib_hash->{'biblio.copyrightdate'} = $item->publication_date;
1067 $bib_hash->{'biblio.title'} = $item->title;
1068 $bib_hash->{'biblio.author'} = $item->author;
1069 $bib_hash->{'biblioitems.isbn'} = $item->item_number_id;
1070 $bib_hash->{'biblioitems.itemtype'} = $item->girfield('stock_category');
1072 # If we have a 13 digit id we are assuming its an ean
1073 # (it may also be an isbn or issn)
1074 if ( $itemid =~ /^\d{13}$/ ) {
1075 $bib_hash->{'biblioitems.ean'} = $itemid;
1076 if ( $itemid =~ /^977/ ) {
1077 $bib_hash->{'biblioitems.issn'} = $itemid;
1080 for my $key ( keys %{$bib_hash} ) {
1081 if ( !defined $bib_hash->{$key} ) {
1082 delete $bib_hash->{$key};
1085 return TransformKohaToMarc
($bib_hash);
1089 sub _create_item_from_quote
{
1090 my ( $item, $quote ) = @_;
1091 my $defalt_classification_source =
1092 C4
::Context
->preference('DefaultClassificationSource');
1094 cn_source
=> $defalt_classification_source,
1098 $item_hash->{booksellerid
} = $quote->vendor_id;
1099 $item_hash->{price
} = $item_hash->{replacementprice
} = $item->price;
1100 $item_hash->{itype
} = $item->girfield('stock_category');
1101 $item_hash->{location
} = $item->girfield('collection_code');
1105 $item_hash->{itemcallnumber
} =
1106 $item->girfield('shelfmark')
1107 || $item->girfield('classification')
1108 || title_level_class
($item);
1110 my $branch = $item->girfield('branch');
1111 $item_hash->{holdingbranch
} = $item_hash->{homebranch
} = $branch;
1124 Module exporting subroutines used in EDI processing for Koha
1128 Subroutines called by batch processing to handle Edifact
1129 messages of various types and related utilities
1133 These routines should really be methods of some object.
1134 get_edifact_ean is a stopgap which should be replaced
1138 =head2 process_quote
1140 process_quote(quote_message);
1142 passed a message object for a quote, parses it creating an order basket
1143 and orderlines in the database
1144 updates the message's status to received in the database and adds the
1147 =head2 process_invoice
1149 process_invoice(invoice_message)
1151 passed a message object for an invoice, add the contained invoices
1152 and update the orderlines referred to in the invoice
1153 As an Edifact invoice is in effect a despatch note this receipts the
1154 appropriate quantities in the orders
1156 no meaningful return value
1158 =head2 process_ordrsp
1160 process_ordrsp(ordrsp_message)
1162 passed a message object for a supplier response, process the contents
1163 If an orderline is cancelled cancel the corresponding orderline in koha
1164 otherwise record the supplier message against it
1166 no meaningful return value
1168 =head2 create_edi_order
1170 create_edi_order( { parameter_hashref } )
1172 parameters must include basketno and ean
1174 branchcode can optionally be passed
1176 returns 1 on success undef otherwise
1178 if the parameter noingest is set the formatted order is returned
1179 and not saved in the database. This functionality is intended for debugging only
1181 =head2 receipt_items
1183 receipt_items( schema_obj, invoice_line, ordernumber)
1185 receipts the items recorded on this invoice line
1187 no meaningful return
1189 =head2 transfer_items
1191 transfer_items(schema, invoice_line, originating_order, receiving_order)
1193 Transfer the items covered by this invoice line from their original
1194 order to another order recording the partial fulfillment of the original
1197 no meaningful return
1199 =head2 get_edifact_ean
1201 $ean = get_edifact_ean();
1203 routine to return the ean.
1207 quote_item(lineitem, quote_message);
1209 Called by process_quote to handle an individual lineitem
1210 Generate the biblios and items if required and orderline linking to them
1212 Returns 1 on success undef on error
1214 Most usual cause of error is a line with no or incorrect budget codes
1215 which woild cause order creation to abort
1216 If other correct lines exist these are processed and the erroneous line os logged
1218 =head2 title_level_class
1220 classmark = title_level_class(edi_item)
1222 Trys to return a title level classmark from a quote message line
1223 Will return a dewey or lcc classmark if one exists according to the
1224 value in DefaultClassificationSource syspref
1226 If unable to returns the shelfmark or classification from the GIR segment
1228 If all else fails returns empty string
1230 =head2 _create_bib_from_quote
1232 marc_record_obj = _create_bib_from_quote(lineitem, quote)
1234 Returns a MARC::Record object based on the info in the quote's lineitem
1236 =head2 _create_item_from_quote
1238 item_hashref = _create_item_from_quote( lineitem, quote)
1240 returns a hashref representing the item fields specified in the quote
1242 =head2 _get_invoiced_price
1244 _get_invoiced_price(line_object)
1246 Returns the net price or an equivalent calculated from line cost / qty
1248 =head2 _discounted_price
1250 ecost = _discounted_price(discount, item_price)
1252 utility subroutine to return a price calculated from the
1253 vendors discount and quoted price
1255 =head2 _check_for_existing_bib
1257 (biblionumber, biblioitemnumber) = _check_for_existing_bib(isbn_or_ean)
1259 passed an isbn or ean attempts to locate a match bib
1260 On success returns biblionumber and biblioitemnumber
1261 On failure returns undefined/an empty list
1265 b = _get_budget(schema_obj, budget_code)
1267 Returns the Aqbudget object for the active budget given the passed budget_code
1268 or undefined if one does not exist
1272 Colin Campbell <colin.campbell@ptfs-europe.com>
1277 Copyright 2014,2015 PTFS-Europe Ltd
1278 This program is free software, You may redistribute it under
1279 under the terms of the GNU General Public License