@@ -, +, @@ --- C4/Edifact.pm | 280 +++++++ C4/Installer/PerlDependencies.pm | 15 + Rebus/EDI.pm | 158 ++++ Rebus/EDI/Custom/Default.pm | 44 ++ Rebus/EDI/System/Koha.pm | 809 +++++++++++++++++++++ Rebus/EDI/Vendor/Default.pm | 384 ++++++++++ acqui/basket.pl | 11 + acqui/edi_ean.pl | 56 ++ admin/edi-accounts.pl | 78 ++ admin/edi-edit.pl | 80 ++ admin/edi_ean_accounts.pl | 71 ++ admin/edi_ean_edit.pl | 71 ++ .../data/mysql/de-DE/mandatory/userpermissions.sql | 1 + .../data/mysql/en/mandatory/userpermissions.sql | 1 + .../data/mysql/es-ES/mandatory/userpermissions.sql | 1 + .../mysql/fr-FR/1-Obligatoire/userpermissions.sql | 1 + .../data/mysql/it-IT/necessari/userpermissions.sql | 1 + installer/data/mysql/kohastructure.sql | 45 ++ .../mysql/nb-NO/1-Obligatorisk/userpermissions.sql | 1 + .../data/mysql/pl-PL/mandatory/userpermissions.sql | 1 + .../ru-RU/mandatory/permissions_and_user_flags.sql | 1 + .../uk-UA/mandatory/permissions_and_user_flags.sql | 1 + installer/data/mysql/updatedatabase.pl | 17 + .../intranet-tmpl/prog/en/includes/admin-menu.inc | 2 + .../intranet-tmpl/prog/en/includes/tools-menu.inc | 3 + .../intranet-tmpl/prog/en/modules/acqui/basket.tt | 8 +- .../intranet-tmpl/prog/en/modules/acqui/edi_ean.tt | 40 + .../prog/en/modules/admin/admin-home.tt | 4 + .../prog/en/modules/admin/edi-accounts.tt | 46 ++ .../prog/en/modules/admin/edi-edit.tt | 102 +++ .../prog/en/modules/admin/edi_ean_accounts.tt | 46 ++ .../prog/en/modules/admin/edi_ean_edit.tt | 74 ++ .../intranet-tmpl/prog/en/modules/tools/edi.tt | 54 ++ .../prog/en/modules/tools/tools-home.tt | 4 + misc/cronjobs/clean_edifiles.pl | 41 ++ misc/cronjobs/edi_quote_cron.pl | 12 + misc/cronjobs/rotate_edi_logs.pl | 22 + misc/edi_files/ptfs-europe-koha-community.CEQ | 1 + tools/edi.pl | 50 ++ 39 files changed, 2636 insertions(+), 1 deletion(-) create mode 100644 C4/Edifact.pm create mode 100644 Rebus/EDI.pm create mode 100644 Rebus/EDI/Custom/Default.pm create mode 100644 Rebus/EDI/System/Koha.pm create mode 100644 Rebus/EDI/Vendor/Bertrams.pm create mode 100644 Rebus/EDI/Vendor/Dawsons.pm create mode 100644 Rebus/EDI/Vendor/Default.pm create mode 100755 acqui/edi_ean.pl create mode 100755 admin/edi-accounts.pl create mode 100755 admin/edi-edit.pl create mode 100755 admin/edi_ean_accounts.pl create mode 100755 admin/edi_ean_edit.pl create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/acqui/edi_ean.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/edi-accounts.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/edi-edit.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/edi_ean_accounts.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/edi_ean_edit.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/edi.tt create mode 100755 misc/cronjobs/clean_edifiles.pl create mode 100755 misc/cronjobs/edi_quote_cron.pl create mode 100755 misc/cronjobs/rotate_edi_logs.pl create mode 100644 misc/edi_files/ptfs-europe-koha-community.CEQ create mode 100755 tools/edi.pl --- a/C4/Edifact.pm +++ a/C4/Edifact.pm @@ -0,0 +1,280 @@ +package C4::Edifact; + +# Copyright 2012 Mark Gavillet +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; +use C4::Context; +use C4::Acquisition; +use Net::FTP; +use C4::Biblio; +use C4::Items; +use Business::ISBN; +use parent qw(Exporter); + +our $VERSION = 0.01; +our $debug = $ENV{DEBUG} || 0; +our @EXPORT = qw( + GetVendorList + DeleteEDIDetails + CreateEDIDetails + UpdateEDIDetails + GetEDIAccounts + GetEDIAccountDetails + GetEDIfactMessageList + CheckVendorFTPAccountExists + GetEDIfactEANs + GetBranchList + delete_edi_ean + create_edi_ean + update_edi_ean +); + +=head1 NAME + +C4::Edifact - Perl Module containing functions for Vendor EDI accounts and EDIfact messages + +=head1 VERSION + +Version 0.01 + +=head1 SYNOPSIS + +use C4::Edifact; + +=head1 DESCRIPTION + +This module contains routines for managing EDI account details for vendors + +=head2 GetVendorList + +Returns a list of vendors from aqbooksellers to populate drop down select menu + +=cut + +sub GetVendorList { + my $dbh = C4::Context->dbh; + my $sth; + $sth = + $dbh->prepare('select id, name from aqbooksellers order by name asc'); + $sth->execute(); + my $vendorlist = $sth->fetchall_arrayref( {} ); + return $vendorlist; +} + +=head2 DeleteEDIDetails + +Remove a vendor's FTP account + +=cut + +sub DeleteEDIDetails { + my $id = shift; + my $dbh = C4::Context->dbh; + my $sth; + if ($id) { + $sth = $dbh->prepare('delete from vendor_edi_accounts where id=?'); + $sth->execute($id); + } + return; +} + +=head2 CreateEDIDetails + +Inserts a new EDI vendor FTP account + +=cut + +sub CreateEDIDetails { + my ( $provider, $description, $host, $user, $pass, $in_dir, $san ) = @_; + my $dbh = C4::Context->dbh; + my $sth; + if ($provider) { + $sth = $dbh->prepare( + 'insert into vendor_edi_accounts + (description, host, username, password, provider, in_dir, san) + values (?,?,?,?,?,?,?)' + ); + $sth->execute( $description, $host, $user, $pass, $provider, $in_dir, + $san ); + } + return; +} + +=head2 UpdateEDIDetails + +Update a vendor's FTP account + +=cut + +sub UpdateEDIDetails { + my ( $editid, $description, $host, $user, $pass, $provider, $in_dir, $san ) + = @_; + my $dbh = C4::Context->dbh; + my $sth; + if ($editid) { + $sth = $dbh->prepare( + 'update vendor_edi_accounts set description=?, host=?, + username=?, password=?, provider=?, in_dir=?, san=? where id=?' + ); + $sth->execute( $description, $host, $user, $pass, $provider, $in_dir, + $san, $editid ); + } + return; +} + +=head2 GetEDIAccounts + +Returns all vendor FTP accounts + +=cut + +sub GetEDIAccounts { + my $dbh = C4::Context->dbh; + my $sth; + $sth = $dbh->prepare( + 'select vendor_edi_accounts.id, aqbooksellers.id as providerid, + aqbooksellers.name as vendor, vendor_edi_accounts.description, + vendor_edi_accounts.last_activity from vendor_edi_accounts inner join + aqbooksellers on vendor_edi_accounts.provider = aqbooksellers.id + order by aqbooksellers.name asc' + ); + $sth->execute(); + my $ediaccounts = $sth->fetchall_arrayref( {} ); + return $ediaccounts; +} + +=head2 GetEDIAccountDetails + +Returns FTP account details for a given vendor + +=cut + +sub GetEDIAccountDetails { + my $id = shift; + my $dbh = C4::Context->dbh; + my $sth; + if ($id) { + $sth = $dbh->prepare('select * from vendor_edi_accounts where id=?'); + $sth->execute($id); + my $edi_details = $sth->fetchrow_hashref; + return $edi_details; + } + return; +} + +=head2 GetEDIfactMessageList + +Returns a list of edifact_messages that have been processed, including the type (quote/order) and status + +=cut + +sub GetEDIfactMessageList { + my $dbh = C4::Context->dbh; + my $sth; + $sth = $dbh->prepare( + 'select edifact_messages.key, edifact_messages.message_type, + DATE_FORMAT(edifact_messages.date_sent,"%d/%m/%Y") as date_sent, + aqbooksellers.id as providerid, aqbooksellers.name as providername, + edifact_messages.status, edifact_messages.basketno from aqbooksellers + inner join vendor_edi_accounts on aqbooksellers.id=vendor_edi_accounts.provider + inner join edifact_messages on edifact_messages.provider=vendor_edi_accounts.id + order by edifact_messages.date_sent desc, edifact_messages.key desc' + ); + $sth->execute(); + my $messagelist = $sth->fetchall_arrayref( {} ); + return $messagelist; +} + +sub CheckVendorFTPAccountExists { + my $booksellerid = shift; + my $dbh = C4::Context->dbh; + my $sth; + my @rows; + my $cnt; + $sth = $dbh->prepare( + 'select count(id) from vendor_edi_accounts where provider=?'); + $sth->execute($booksellerid); + if ( $sth->rows == 0 ) { + return undef; + } + else { + return 1; + } +} + +sub GetEDIfactEANs { + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( +'select branches.branchname, edifact_ean.ean, edifact_ean.branchcode from + branches inner join edifact_ean on edifact_ean.branchcode=branches.branchcode + order by branches.branchname asc' + ); + $sth->execute(); + my $eans = $sth->fetchall_arrayref( {} ); + return $eans; +} + +sub GetBranchList { + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( + 'select branches.branchname, branches.branchcode from branches + order by branches.branchname asc' + ); + $sth->execute(); + my $branches = $sth->fetchall_arrayref( {} ); + return $branches; +} + +sub delete_edi_ean { + my ( $branchcode, $ean ) = @_; + my $dbh = C4::Context->dbh; + my $sth = + $dbh->prepare('delete from edifact_ean where branchcode=? and ean=?'); + $sth->execute( $branchcode, $ean ); + return; +} + +sub create_edi_ean { + my ( $branchcode, $ean ) = @_; + my $dbh = C4::Context->dbh; + my $sth = + $dbh->prepare('insert into edifact_ean (branchcode,ean) values (?,?)'); + $sth->execute( $branchcode, $ean ); + return; +} + +sub update_edi_ean { + my ( $branchcode, $ean, $oldbranchcode, $oldean ) = @_; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( + 'update edifact_ean set branchcode=?, ean=? where branchcode=? and ean=?' + ); + $sth->execute( $branchcode, $ean, $oldbranchcode, $oldean ); + return; +} + +1; + +__END__ + +=head1 AUTHOR + +Mark Gavillet + +=cut --- a/C4/Installer/PerlDependencies.pm +++ a/C4/Installer/PerlDependencies.pm @@ -534,6 +534,21 @@ our $PERL_DEPS = { 'required' => '1', 'min_ver' => '0.03', }, + 'Business::Edifact::Interchange' => { + 'usage' => 'Core', + 'required' => '1', + 'min_ver' => '0.02', + }, + 'Net::FTP' => { + 'usage' => 'Core', + 'required' => '1', + 'min_ver' => '2.77', + }, + 'Net::FTP::File' => { + 'usage' => 'Core', + 'required' => '1', + 'min_ver' => '0.06', + }, }; 1; --- a/Rebus/EDI.pm +++ a/Rebus/EDI.pm @@ -0,0 +1,158 @@ +package Rebus::EDI; + +# Copyright 2012 Mark Gavillet + +use strict; +use warnings; + +=head1 NAME + +Rebus::EDI + +=head1 VERSION + +Version 0.01 + +=cut + +our $VERSION = '0.01'; + +our @vendors = ( + { + name => 'Bertrams', + san => '0143731', + ean => '', + module => 'Bertrams' + }, + { + name => 'Bertrams', + san => '5013546025078', + ean => '', + module => 'Bertrams' + }, + { + name => 'Dawsons', + san => '', + ean => '5013546027856', + module => 'Dawsons' + }, + { + name => 'Coutts', + san => '', + ean => '5013546048686', + module => 'Default' + }, + { + name => 'Tomlinsons', + san => '', + ean => '5033075063552', + module => 'Default' + }, + { + name => 'PTFS Europe', + san => '', + ean => '5011234567890', + module => 'Default' + }, +); + +sub new { + my $class = shift; + my $system = shift; + my $self = {}; + $self->{system} = 'koha'; + use Rebus::EDI::System::Koha; + $self->{edi_system} = Rebus::EDI::System::Koha->new(); + bless $self, $class; + return $self; +} + +sub list_vendors { + return @vendors; +} + +sub retrieve_quotes { + my $self = shift; + my @vendor_ftp_accounts = $self->{edi_system}->retrieve_vendor_ftp_accounts; + my @downloaded_quotes = + $self->{edi_system}->download_quotes( \@vendor_ftp_accounts ); + my $processed_quotes = + $self->{edi_system}->process_quotes( \@downloaded_quotes ); +} + +sub send_orders { + my ( $self, $order_id, $ean ) = @_; + my $orders = $self->{edi_system}->retrieve_orders($order_id); + my $order_details = + $self->{edi_system}->retrieve_order_details( $orders, $ean ); + foreach my $order ( @{$order_details} ) { + my $module = $order->{module}; + require "Rebus/EDI/Vendor/$module.pm"; + $module = "Rebus::EDI::Vendor::$module"; + import $module; + my $vendor_module = $module->new(); + my $order_message = $vendor_module->create_order_message($order); + my $order_file = + $self->{edi_system} + ->create_order_file( $order_message, $order->{order_id} ); + } +} + +sub string35escape { + my $string = shift; + my $colon_string; + my @sections; + if ( length($string) > 35 ) { + my ( $chunk, $stringlength ) = ( 35, length($string) ); + for ( my $counter = 0 ; $counter < $stringlength ; $counter += $chunk ) + { + push @sections, substr( $string, $counter, $chunk ); + } + foreach my $section (@sections) { + $colon_string .= $section . ":"; + } + chop($colon_string); + } + else { + $colon_string = $string; + } + return $colon_string; +} + +sub escape_reserved { + my $string = shift; + if ( $string ne "" ) { + $string =~ s/\?/\?\?/g; + $string =~ s/\'/\?\'/g; + $string =~ s/\:/\?\:/g; + $string =~ s/\+/\?\+/g; + return $string; + } + else { + return; + } +} + +sub cleanisbn { + my $isbn = shift; + if ( $isbn ne "" ) { + my $i = index( $isbn, '(' ); + if ( $i > 1 ) { + $isbn = substr( $isbn, 0, ( $i - 1 ) ); + } + if ( index( $isbn, "|" ) != -1 ) { + my @isbns = split( /\|/, $isbn ); + $isbn = $isbns[0]; + } + + #$isbn=__PACKAGE__->escape_reserved($isbn); + $isbn =~ s/^\s+//; + $isbn =~ s/\s+$//; + return $isbn; + } + else { + return; + } +} + +1; --- a/Rebus/EDI/Custom/Default.pm +++ a/Rebus/EDI/Custom/Default.pm @@ -0,0 +1,44 @@ +package Rebus::EDI::Custom::Default; + +# Copyright 2012 Mark Gavillet + +use strict; +use warnings; + +use parent qw(Exporter); + +=head1 NAME + +Rebus::EDI::Custom::Default + +=head1 VERSION + +Version 0.01 + +=cut + +our $VERSION = '0.01'; + +sub new { + my $class = shift; + my $self = {}; + bless $self, $class; + return $self; +} + +sub transform_local_quote_copy { + my ( $self, $item ) = @_; + + ### default - return the item without transformations + return $item; +} + +sub lsq_identifier { + ### use CCODE authorised values in LSQ segment + # return 'ccode'; + + ### use LOC authorised values in LSQ segment + return 'location'; +} + +1; --- a/Rebus/EDI/System/Koha.pm +++ a/Rebus/EDI/System/Koha.pm @@ -0,0 +1,809 @@ +package Rebus::EDI::System::Koha; + +# Copyright 2012 Mark Gavillet + +use strict; +use warnings; + +=head1 NAME + +Rebus::EDI::System::Koha + +=head1 VERSION + +Version 0.01 + +=cut + +use C4::Context; + +our $VERSION = '0.01'; + +### Evergreen +#our $edidir = "/tmp/"; + +### Koha +our $edidir = "$ENV{'PERL5LIB'}/misc/edi_files/"; + +our $ftplogfile = "$edidir/edi_ftp.log"; +our $quoteerrorlogfile = "$edidir/edi_quote_error.log"; +our $edi_quote_user = 0; + +sub new { + my $class = shift; + my $self = {}; + bless $self, $class; + return $self; +} + +sub retrieve_vendor_ftp_accounts { + my $self = shift; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( + 'select vendor_edi_accounts.id as edi_account_id, + aqbooksellers.id as account_id, aqbooksellers.name as vendor, + vendor_edi_accounts.host as server, vendor_edi_accounts.username as ftpuser, + vendor_edi_accounts.password as ftppass, vendor_edi_accounts.in_dir as ftpdir + from vendor_edi_accounts inner join aqbooksellers on + vendor_edi_accounts.provider = aqbooksellers.id' + ); + $sth->execute(); + my $set = $sth->fetchall_arrayref( {} ); + my @accounts; + my $new_account; + + foreach my $account (@$set) { + $new_account = { + account_id => $account->{account_id}, + edi_account_id => $account->{edi_account_id}, + vendor => $account->{vendor}, + server => $account->{server}, + ftpuser => $account->{ftpuser}, + ftppass => $account->{ftppass}, + ftpdir => $account->{ftpdir}, + po_org_unit => 0, + }; + push( @accounts, $new_account ); + } + return @accounts; +} + +sub download_quotes { + my ( $self, $ftp_accounts ) = @_; + my @local_files; + foreach my $account (@$ftp_accounts) { + + #get vendor details + print "server: " . $account->{server} . "\n"; + print "account: " . $account->{vendor} . "\n"; + + #get files + use Net::FTP; + my $newerr; + my @ERRORS; + my @files; + open my $ediftplog, '>>', $ftplogfile + or die "Could not open $ftplogfile\n"; + my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(time); + printf $ediftplog "\n\n%4d-%02d-%02d %02d:%02d:%02d\n-----\n", + $year + 1900, $mon + 1, $mday, $hour, $min, $sec; + print $ediftplog "Connecting to " . $account->{server} . "... "; + my $ftp = + Net::FTP->new( $account->{server}, Timeout => 10, Passive => 1 ) + or $newerr = 1; + push @ERRORS, "Can't ftp to " . $account->{server} . ": $!\n" + if $newerr; + myerr(@ERRORS) if $newerr; + + if ( !$newerr ) { + $newerr = 0; + print $ediftplog "connected.\n"; + + $ftp->login( $account->{ftpuser}, $account->{ftppass} ) + or $newerr = 1; + print $ediftplog "Getting file list\n"; + push @ERRORS, "Can't login to " . $account->{server} . ": $!\n" + if $newerr; + $ftp->quit if $newerr; + myerr(@ERRORS) if $newerr; + if ( !$newerr ) { + print $ediftplog "Logged in\n"; + $ftp->cwd( $account->{ftpdir} ) or $newerr = 1; + push @ERRORS, + "Can't cd in server " . $account->{server} . " $!\n" + if $newerr; + myerr(@ERRORS) if $newerr; + $ftp->quit if $newerr; + + @files = $ftp->ls or $newerr = 1; + push @ERRORS, + "Can't get file list from server " + . $account->{server} . " $!\n" + if $newerr; + myerr(@ERRORS) if $newerr; + if ( !$newerr ) { + print $ediftplog "Got file list\n"; + foreach (@files) { + my $filename = $_; + if ( ( index lc($filename), '.ceq' ) > -1 ) { + my $description = sprintf "%s/%s", + $account->{server}, $filename; + print $ediftplog "Found file: $description - "; + + # deduplicate vs. acct/filenames already in DB + my $hits = + find_duplicate_quotes( $account->{edi_account_id}, + $account->{ftpdir}, $filename ); + + my $match = 0; + if ( scalar(@$hits) ) { + print $ediftplog + "File already retrieved. Skipping.\n"; + $match = 1; + } + if ( $match ne 1 ) { + chdir "$edidir"; + $ftp->get($filename) or $newerr = 1; + push @ERRORS, + "Can't transfer file ($filename) from " + . $account->{server} . " $!\n" + if $newerr; + $ftp->quit if $newerr; + myerr(@ERRORS) if $newerr; + if ( !$newerr ) { + print $ediftplog "File retrieved\n"; + open my $fh, '<', "$edidir/$filename" + or die "Couldn't open file: $!\n"; + my $message_content = join( "", <$fh> ); + close $fh; + my $logged_quote = LogQuote( + $message_content, + $account->{ftpdir} . "/" . $filename, + $account->{server}, + $account->{edi_account_id} + ); + my $quote_file = { + filename => $filename, + account_id => $account->{account_id}, + po_org_unit => $account->{po_org_unit}, + edi_quote_user => $edi_quote_user, + logged_quote_id => $logged_quote, + edi_account_id => + $account->{edi_account_id}, + }; + push( @local_files, $quote_file ); + } + } + } + } + } + } + + $ftp->quit; + } + $newerr = 0; + } + return @local_files; +} + +sub myerr { + my @errors = shift; + open my $ediftplog, '>>', $ftplogfile or die "Could not open $ftplogfile\n"; + print $ediftplog "Error: ", @errors; + close $ediftplog; +} + +sub find_duplicate_quotes { + my ( $edi_account_id, $ftpdir, $filename ) = @_; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( + 'select edifact_messages.key from edifact_messages + inner join vendor_edi_accounts on vendor_edi_accounts.provider=edifact_messages.provider + where vendor_edi_accounts.id=? and edifact_messages.remote_file=? and status<>?' + ); + $sth->execute( $edi_account_id, $ftpdir . "/" . $filename, 'Processed' ); + my $hits = $sth->fetchall_arrayref( {} ); + return $hits; +} + +# updates last activity in acq.edi_account and writes a new entry to acq.edi_message +sub LogQuote { + my ( $content, $remote, $server, $account_or_id ) = @_; + $content or return; + my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(time); + my $last_activity = + sprintf( "%4d-%02d-%02d", $year + 1900, $mon + 1, $mday ); + my $account = record_activity( $account_or_id, $last_activity ); + my $message_type = ( $content =~ /'UNH\+\w+\+(\S{6}):/ ) ? $1 : 'QUOTES'; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( + 'insert into edifact_messages (message_type, date_sent, provider, + status, edi, remote_file) values (?,?,?,?,?,?)' + ); + $sth->execute( $message_type, $last_activity, $account, 'Received', + $content, $remote ); + my $insert_id = + $dbh->last_insert_id( undef, undef, qw(edifact_messages key), undef ); + + return $insert_id; +} + +sub update_quote_status { + my ( $quote_id, $edi_account_id, $basketno ) = @_; + my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(time); + my $last_activity = sprintf '%4d-%02d-%02d', $year + 1900, $mon + 1, $mday; + my $account = record_activity( $edi_account_id, $last_activity ); + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( + 'update edifact_messages set edifact_messages.status=?, + basketno=? where edifact_messages.key=?' + ); + $sth->execute( 'Processed', $basketno, $quote_id ); +} + +sub record_activity { + my ( $account_or_id, $last_activity ) = @_; + $account_or_id or return; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( + 'update vendor_edi_accounts set last_activity=? where + id=?' + ); + $sth->execute( $last_activity, $account_or_id ); + $sth = $dbh->prepare('select provider from vendor_edi_accounts where id=?'); + $sth->execute($account_or_id); + my @result; + my $provider; + + while ( @result = $sth->fetchrow_array() ) { + $provider = $result[0]; + } + return $provider; +} + +sub process_quotes { + my ( $self, $quotes ) = @_; + foreach my $quote (@$quotes) { + my $vendor_san = get_vendor_san( $quote->{account_id} ); + my $module = get_vendor_module($vendor_san); + $module or return; + require "Rebus/EDI/Vendor/$module.pm"; + $module = "Rebus::EDI::Vendor::$module"; + import $module; + my $vendor_module = $module->new(); + my @parsed_quote = $vendor_module->parse_quote($quote); + use C4::Acquisition; + use C4::Biblio; + use C4::Items; + my $order_id = + NewBasket( $quote->{account_id}, 0, $quote->{filename}, '', '', '' ); + + foreach my $item (@parsed_quote) { + foreach my $copy ( @{ $item->{copies} } ) { + my $quote_copy = { + author => $item->{author}, + price => $item->{price}, + ecost => get_discounted_price( + $quote->{account_id}, $item->{price} + ), + llo => $copy->{llo}, + lfn => $copy->{lfn}, + lsq => $copy->{lsq}, + lst => $copy->{lst}, + lcl => $copy->{lcl}, + budget_id => get_budget_id( $copy->{lfn} ), + title => $item->{title}, + isbn => $item->{isbn}, + publisher => $item->{publisher}, + year => $item->{year}, + }; + use Rebus::EDI::Custom::Default; + my $local_transform = Rebus::EDI::Custom::Default->new(); + my $koha_copy = + $local_transform->transform_local_quote_copy($quote_copy); + + my $lsq_identifier = $local_transform->lsq_identifier(); + + # create biblio record + my $record = TransformKohaToMarc( + { + "biblio.title" => $koha_copy->{title}, + "biblio.author" => $koha_copy->{author} + ? $koha_copy->{author} + : "", + "biblio.seriestitle" => "", + "biblioitems.isbn" => $koha_copy->{isbn} + ? $koha_copy->{isbn} + : "", + "biblioitems.publishercode" => $koha_copy->{publisher} + ? $koha_copy->{publisher} + : "", + "biblioitems.publicationyear" => $koha_copy->{year} + ? $koha_copy->{year} + : "", + "biblio.copyrightdate" => $koha_copy->{year} + ? $koha_copy->{year} + : "", + "biblioitems.cn_source" => "ddc", + "items.cn_source" => "ddc", + "items.notforloan" => "-1", + "items.$lsq_identifier" => $koha_copy->{lsq}, + "items.homebranch" => $koha_copy->{llo}, + "items.holdingbranch" => $koha_copy->{llo}, + "items.booksellerid" => $quote->{account_id}, + "items.price" => $koha_copy->{price}, + "items.replacementprice" => $koha_copy->{price}, + "items.itemcallnumber" => $koha_copy->{lcl}, + "items.itype" => $koha_copy->{lst}, + "items.cn_sort" => "", + } + ); + + #check if item already exists in catalogue + my ( $biblionumber, $bibitemnumber ) = + check_order_item_exists( $item->{isbn} ); + + if ( !defined $biblionumber ) { + + # create the record in catalogue, with framework '' + ( $biblionumber, $bibitemnumber ) = + AddBiblio( $record, '' ); + } + + # create order line + my %orderinfo = ( + basketno => $order_id, + ordernumber => "", + subscription => "no", + uncertainprice => 0, + biblionumber => $biblionumber, + title => $koha_copy->{title}, + quantity => 1, + biblioitemnumber => $bibitemnumber, + rrp => $koha_copy->{price}, + ecost => $koha_copy->{ecost}, + sort1 => "", + sort2 => "", + booksellerinvoicenumber => $item->{item_reference}, + listprice => $koha_copy->{price}, + branchcode => $koha_copy->{llo}, + budget_id => $koha_copy->{budget_id}, + ); + + my $orderinfo = \%orderinfo; + + my ( $retbasketno, $ordernumber ) = NewOrder($orderinfo); + + # now, add items if applicable + if ( C4::Context->preference('AcqCreateItem') eq 'ordering' ) { + my $itemnumber; + ( $biblionumber, $bibitemnumber, $itemnumber ) = + AddItemFromMarc( $record, $biblionumber ); + NewOrderItem( $itemnumber, $ordernumber ); + } + } + } + update_quote_status( $quote->{logged_quote_id}, + $quote->{edi_account_id}, $order_id ); + ### manipulate quote file on remote server + my $vendor_ftp_account = + get_vendor_ftp_account( $quote->{edi_account_id} ); + $vendor_module->post_process_quote_file( $quote->{filename}, + $vendor_ftp_account ); + return 1; + + } +} + +sub get_vendor_ftp_account { + my $edi_account_id = shift; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( + 'select host,username,password,in_dir from vendor_edi_accounts + where id=?' + ); + $sth->execute($edi_account_id); + my @result; + my $account; + while ( @result = $sth->fetchrow_array() ) { + $account = { + host => $result[0], + username => $result[1], + password => $result[2], + in_dir => $result[3], + }; + } + return $account; +} + +sub get_discounted_price { + my ( $booksellerid, $price ) = @_; + my $dbh = C4::Context->dbh; + my @discount; + my $ecost; + my $percentage; + my $sth = $dbh->prepare('select discount from aqbooksellers where id=?'); + $sth->execute($booksellerid); + while ( @discount = $sth->fetchrow_array() ) { + $percentage = $discount[0]; + } + $ecost = ( $price - ( ( $percentage * $price ) / 100 ) ); + return $ecost; +} + +sub get_budget_id { + my $fundcode = shift; + my $dbh = C4::Context->dbh; + my @funds; + my $ecost; + my $budget_id; + my $sth = + $dbh->prepare('select budget_id from aqbudgets where budget_code=?'); + $sth->execute($fundcode); + while ( @funds = $sth->fetchrow_array() ) { + $budget_id = $funds[0]; + } + return $budget_id; +} + +sub get_vendor_san { + my $vendor_id = shift; + my $dbh = C4::Context->dbh; + my $sth = + $dbh->prepare('select san from vendor_edi_accounts where provider=?'); + $sth->execute($vendor_id); + my @result; + my $san; + while ( @result = $sth->fetchrow_array() ) { + $san = $result[0]; + } + return $san; +} + +sub get_vendor_module { + my $san = shift; + my $module; + use Rebus::EDI; + my @vendor_list = Rebus::EDI::list_vendors(); + foreach my $vendor (@vendor_list) { + if ( $san eq $vendor->{san} || $san eq $vendor->{ean} ) { + $module = $vendor->{module}; + last; + } + } + return $module; +} + +sub check_order_item_exists { + my $isbn = shift; + my $dbh = C4::Context->dbh; + my $sth; + my @matches; + my $biblionumber; + my $bibitemnumber; + $sth = $dbh->prepare( + 'select biblionumber, biblioitemnumber from biblioitems where isbn=?'); + $sth->execute($isbn); + + while ( @matches = $sth->fetchrow_array() ) { + $biblionumber = $matches[0]; + $bibitemnumber = $matches[1]; + } + if ($biblionumber) { + return $biblionumber, $bibitemnumber; + } + else { + use Rebus::EDI; + use Business::ISBN; + my $edi = Rebus::EDI->new(); + $isbn = $edi->cleanisbn($isbn); + if ( length($isbn) == 10 ) { + $isbn = Business::ISBN->new($isbn); + if ($isbn) { + if ( $isbn->is_valid ) { + $isbn = ( $isbn->as_isbn13 )->isbn; + $sth->execute($isbn); + while ( @matches = $sth->fetchrow_array() ) { + $biblionumber = $matches[0]; + $bibitemnumber = $matches[1]; + } + } + } + } + elsif ( length($isbn) == 13 ) { + $isbn = Business::ISBN->new($isbn); + if ($isbn) { + if ( $isbn->is_valid ) { + $isbn = ( $isbn->as_isbn10 )->isbn; + $sth->execute($isbn); + while ( @matches = $sth->fetchrow_array() ) { + $biblionumber = $matches[0]; + $bibitemnumber = $matches[1]; + } + } + } + } + return $biblionumber, $bibitemnumber; + } +} + +sub retrieve_orders { + my ( $self, $order_id ) = @_; + my $dbh = C4::Context->dbh; + my @active_orders; + + ## retrieve basic order details + my $sth = $dbh->prepare( + 'select booksellerid as provider from aqbasket where basketno=?'); + $sth->execute($order_id); + my $orders = $sth->fetchall_arrayref( {} ); + + foreach my $order ( @{$orders} ) { + push @active_orders, + { order_id => $order_id, provider_id => $order->{provider} }; + } + return \@active_orders; +} + +sub retrieve_order_details { + my ( $self, $orders, $ean ) = @_; + my @fleshed_orders; + foreach my $order ( @{$orders} ) { + my $fleshed_order; + $fleshed_order = { + order_id => $order->{order_id}, + provider_id => $order->{provider_id} + }; + + ## retrieve module for vendor + my $dbh = C4::Context->dbh; + my $sth = + $dbh->prepare('select san from vendor_edi_accounts where provider=?'); + $sth->execute( $order->{provider_id} ); + my @result; + my $san; + while ( @result = $sth->fetchrow_array() ) { + $san = $result[0]; + } + $fleshed_order->{'module'} = get_vendor_module($san); + $fleshed_order->{'san_or_ean'} = $san; + $fleshed_order->{'org_san'} = $ean; + $fleshed_order->{'quote_or_order'} = + quote_or_order( $order->{order_id} ); + my @lineitems = get_order_lineitems( $order->{order_id} ); + $fleshed_order->{'lineitems'} = \@lineitems; + + push @fleshed_orders, $fleshed_order; + } + return \@fleshed_orders; +} + +sub create_order_file { + my ( $self, $order_message, $order_id ) = @_; + my $filename = "$edidir/ediorder_$order_id.CEP"; + open my $ediorder, '>', $filename; + print $ediorder $order_message; + close $ediorder; + my $vendor_ftp_account = get_vendor_ftp_account_by_order_id($order_id); + my $sent_order = + send_order_message( $filename, $vendor_ftp_account, $order_message, + $order_id ); + return $filename; +} + +sub get_vendor_ftp_account_by_order_id { + my $order_id = shift; + my $vendor_ftp_account; + my @result; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( + 'select vendor_edi_accounts.* from vendor_edi_accounts, aqbasket + where vendor_edi_accounts.provider=aqbasket.booksellerid and aqbasket.basketno=?' + ); + $sth->execute($order_id); + while ( @result = $sth->fetchrow_array() ) { + $vendor_ftp_account->{id} = $result[0]; + $vendor_ftp_account->{label} = $result[1]; + $vendor_ftp_account->{host} = $result[2]; + $vendor_ftp_account->{username} = $result[3]; + $vendor_ftp_account->{password} = $result[4]; + $vendor_ftp_account->{path} = $result[7]; + $vendor_ftp_account->{last_activity} = $result[5]; + $vendor_ftp_account->{provider} = $result[6]; + $vendor_ftp_account->{in_dir} = $result[7]; + $vendor_ftp_account->{edi_account_id} = $result[0]; + } + return $vendor_ftp_account; +} + +sub send_order_message { + my ( $filename, $ftpaccount, $order_message, $order_id ) = @_; + my @ERRORS; + my $newerr; + my $result; + + open my $ediftplog, '>>', $ftplogfile or die "Could not open $ftplogfile\n"; + my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(time); + printf $ediftplog "\n\n%4d-%02d-%02d %02d:%02d:%02d\n-----\n", $year + 1900, + $mon + 1, $mday, $hour, $min, $sec; + + # check edi order file exists + if ( -e $filename ) { + use Net::FTP; + + print $ediftplog "Connecting to " . $ftpaccount->{host} . "... "; + + # connect to ftp account + my $ftp = + Net::FTP->new( $ftpaccount->{host}, Timeout => 10, Passive => 1 ) + or $newerr = 1; + push @ERRORS, "Can't ftp to " . $ftpaccount->{host} . ": $!\n" + if $newerr; + myerr(@ERRORS) if $newerr; + if ( !$newerr ) { + $newerr = 0; + print $ediftplog "connected.\n"; + + # login + $ftp->login( "$ftpaccount->{username}", "$ftpaccount->{password}" ) + or $newerr = 1; + $ftp->quit if $newerr; + print $ediftplog "Logging in...\n"; + push @ERRORS, "Can't login to " . $ftpaccount->{host} . ": $!\n" + if $newerr; + myerr(@ERRORS) if $newerr; + if ( !$newerr ) { + print $ediftplog "Logged in\n"; + + # cd to directory + $ftp->cwd("$ftpaccount->{path}") or $newerr = 1; + push @ERRORS, + "Can't cd in server " . $ftpaccount->{host} . " $!\n" + if $newerr; + myerr(@ERRORS) if $newerr; + $ftp->quit if $newerr; + + # put file + if ( !$newerr ) { + $newerr = 0; + $ftp->put($filename) or $newerr = 1; + push @ERRORS, + "Can't write order file to server " + . $ftpaccount->{host} . " $!\n" + if $newerr; + myerr(@ERRORS) if $newerr; + $ftp->quit if $newerr; + if ( !$newerr ) { + print $ediftplog + "File: $filename transferred successfully\n"; + $ftp->quit; + unlink($filename); + record_activity( $ftpaccount->{id} ); + log_order( + $order_message, + $ftpaccount->{path} . substr( $filename, 4 ), + $ftpaccount->{edi_account_id}, + $order_id + ); + + return $result; + } + } + } + } + } + else { + print $ediftplog "Order file $filename does not exist\n"; + } +} + +sub log_order { + my ( $content, $remote, $edi_account_id, $order_id ) = @_; + my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(time); + my $date_sent = sprintf( "%4d-%02d-%02d", $year + 1900, $mon + 1, $mday ); + + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( +'insert into edifact_messages (message_type,date_sent,provider,status,basketno, + edi,remote_file) values (?,?,?,?,?,?,?)' + ); + $sth->execute( 'ORDER', $date_sent, $edi_account_id, 'Sent', $order_id, + $content, $remote ); +} + +sub quote_or_order { + my $order_id = shift; + my @result; + my $quote_or_order; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( + 'select edifact_messages.key from edifact_messages + where basketno=? and message_type=?' + ); + $sth->execute( $order_id, 'QUOTES' ); + if ( $sth->rows == 0 ) { + $quote_or_order = 'o'; + } + else { + $quote_or_order = 'q'; + } + return $quote_or_order; +} + +sub get_order_lineitems { + my $order_id = shift; + use C4::Acquisition; + my @lineitems = GetOrders($order_id); + my @fleshed_lineitems; + foreach my $lineitem (@lineitems) { + use Rebus::EDI; + my $clean_isbn = Rebus::EDI::cleanisbn( $lineitem->{isbn} ); + my $fleshed_lineitem; + $fleshed_lineitem->{binding} = 'O'; + $fleshed_lineitem->{currency} = 'GBP'; + $fleshed_lineitem->{id} = $lineitem->{ordernumber}; + $fleshed_lineitem->{qli} = $lineitem->{booksellerinvoicenumber}; + $fleshed_lineitem->{rff} = $order_id . "/" . $fleshed_lineitem->{id}; + $fleshed_lineitem->{isbn} = $clean_isbn; + $fleshed_lineitem->{title} = $lineitem->{title}; + $fleshed_lineitem->{author} = $lineitem->{author}; + $fleshed_lineitem->{publisher} = $lineitem->{publishercode}; + $fleshed_lineitem->{year} = $lineitem->{copyrightdate}; + $fleshed_lineitem->{price} = sprintf "%.2f", $lineitem->{listprice}; + $fleshed_lineitem->{quantity} = '1'; + + my @lineitem_copies; + my $fleshed_lineitem_detail; + my ( $branchcode, $callnumber, $itype, $location, $fund ) = + get_lineitem_additional_info( $lineitem->{ordernumber} ); + $fleshed_lineitem_detail->{llo} = $branchcode; + $fleshed_lineitem_detail->{lfn} = $fund; + $fleshed_lineitem_detail->{lsq} = $location; + $fleshed_lineitem_detail->{lst} = $itype; + $fleshed_lineitem_detail->{lcl} = $callnumber; + $fleshed_lineitem_detail->{note} = $lineitem->{notes}; + push( @lineitem_copies, $fleshed_lineitem_detail ); + + $fleshed_lineitem->{copies} = \@lineitem_copies; + push( @fleshed_lineitems, $fleshed_lineitem ); + } + return @fleshed_lineitems; +} + +sub get_lineitem_additional_info { + my $ordernumber = shift; + my @rows; + my $homebranch; + my $callnumber; + my $itype; + my $location; + my $fund; + use Rebus::EDI::Custom::Default; + my $local_transform = Rebus::EDI::Custom::Default->new(); + my $lsq_identifier = $local_transform->lsq_identifier(); + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( + "select items.homebranch, items.itemcallnumber, items.itype, + items.$lsq_identifier from items inner join aqorders_items on + aqorders_items.itemnumber=items.itemnumber where aqorders_items.ordernumber=?" + ); + $sth->execute($ordernumber); + + while ( @rows = $sth->fetchrow_array() ) { + $homebranch = $rows[0]; + $callnumber = $rows[1]; + $itype = $rows[2]; + $location = $rows[3]; + } + $sth = $dbh->prepare( + "select aqbudgets.budget_code from aqbudgets inner join aqorders on + aqorders.budget_id=aqbudgets.budget_id where aqorders.ordernumber=?" + ); + $sth->execute($ordernumber); + while ( @rows = $sth->fetchrow_array() ) { + $fund = $rows[0]; + } + return $homebranch, $callnumber, $itype, $location, $fund; +} + +1; --- a/Rebus/EDI/Vendor/Default.pm +++ a/Rebus/EDI/Vendor/Default.pm @@ -0,0 +1,384 @@ +package Rebus::EDI::Vendor::Default; + +# Copyright 2012 Mark Gavillet + +use strict; +use warnings; + +use parent qw(Exporter); +our @EXPORT = qw( + test +); + +use Business::Edifact::Interchange; +### Evergreen +#our $edidir = "/tmp/"; + +### Koha +our $edidir = "$ENV{'PERL5LIB'}/misc/edi_files/"; + +=head1 NAME + +Rebus::EDI::Vendor::Default + +=head1 VERSION + +Version 0.01 + +=cut + +our $VERSION = '0.01'; + +sub new { + my $class = shift; + my $self = {}; + bless $self, $class; + return $self; +} + +sub parse_quote { + my ( $self, $quote ) = @_; + my $edi = Business::Edifact::Interchange->new; + my @parsed_quote; + $edi->parse_file( $edidir . $quote->{filename} ); + my $messages = $edi->messages(); + my $message_count = @{$messages}; + my $count; + + for ( $count = 0 ; $count < $message_count ; $count++ ) { + my $items = $messages->[$count]->items(); + + foreach my $item ( @{$items} ) { + my $parsed_item = { + author => $item->author_surname . ", " + . $item->author_firstname, + title => $item->title, + isbn => $item->{item_number}, + price => $item->{price}->{price}, + publisher => $item->publisher, + year => $item->date_of_publication, + item_reference => $item->{item_reference}[0][1], + copies => '', + }; + my $quantity = $item->{quantity}; + my @copies; + for ( my $i = 0 ; $i < $item->{quantity} ; $i++ ) { + my $llo = $item->{related_numbers}->[$i]->{LLO}->[0]; + my $lfn = $item->{related_numbers}->[$i]->{LFN}->[0]; + my $lsq = $item->{related_numbers}->[$i]->{LSQ}->[0]; + my $lst = $item->{related_numbers}->[$i]->{LST}->[0]; + my $shelfmark = $item->shelfmark; + my $ftxlin; + my $ftxlno; + if ( $item->{free_text}->{qualifier} eq "LIN" ) { + $ftxlin = $item->{free_text}->{text}; + } + if ( $item->{free_text}->{qualifier} eq "LNO" ) { + $ftxlno = $item->{free_text}->{text}; + } + my $note; + if ($ftxlin) { + $note = $ftxlin; + } + if ($ftxlno) { + $note = $ftxlno; + } + my $parsed_copy = { + llo => $llo, + lfn => $lfn, + lsq => $lsq, + lst => $lst, + shelfmark => $shelfmark, + note => $note, + }; + push( @copies, $parsed_copy ); + } + $parsed_item->{"copies"} = \@copies; + push( @parsed_quote, $parsed_item ); + } + } + return @parsed_quote; +} + +sub create_order_message { + my ( $self, $order ) = @_; + my @datetime = localtime(time); + my $longyear = ( $datetime[5] + 1900 ); + my $shortyear = sprintf "%02d", ( $datetime[5] - 100 ); + my $date = sprintf "%02d%02d", ( $datetime[4] + 1 ), $datetime[3]; + my $hourmin = sprintf "%02d%02d", $datetime[2], $datetime[1]; + my $year = ( $datetime[5] - 100 ); + my $month = sprintf "%02d", ( $datetime[4] + 1 ); + my $linecount = 0; + my $segment = 0; + my $exchange = int( rand(99999999999999) ); + my $ref = int( rand(99999999999999) ); + + ### opening header + my $order_message = "UNA:+.? '"; + + ### Library SAN or EAN + $order_message .= "UNB+UNOC:2"; + if ( length( $order->{org_san} ) != 13 ) { + $order_message .= "+" . $order->{org_san} . ":31B"; # use SAN qualifier + } + else { + $order_message .= "+" . $order->{org_san} . ":14"; # use EAN qualifier + } + + ### Vendor SAN or EAN + if ( length( $order->{san_or_ean} ) != 13 ) { + $order_message .= + "+" . $order->{san_or_ean} . ":31B"; # use SAN qualifier + } + else { + $order_message .= + "+" . $order->{san_or_ean} . ":14"; # use EAN qualifier + } + + ### date/time, exchange reference number + $order_message .= + "+$shortyear$date:$hourmin+" . $exchange . "++ORDERS+++EANCOM'"; + + ### message reference number + $order_message .= "UNH+" . $ref . "+ORDERS:D:96A:UN:EAN008'"; + $segment++; + + ### Order number and quote confirmation reference (if in response to quote) + if ( $order->{quote_or_order} eq 'q' ) { + $order_message .= "BGM+22V+" . $order->{order_id} . "+9'"; + $segment++; + } + else { + $order_message .= "BGM+220+" . $order->{order_id} . "+9'"; + $segment++; + } + + ### Date of message + $order_message .= "DTM+137:$longyear$date:102'"; + $segment++; + + ### Library Address Identifier (SAN or EAN) + if ( length( $order->{org_san} ) != 13 ) { + $order_message .= "NAD+BY+" . $order->{org_san} . "::31B'"; + $segment++; + } + else { + $order_message .= "NAD+BY+" . $order->{org_san} . "::9'"; + $segment++; + } + + ### Vendor address identifier (SAN or EAN) + if ( length( $order->{san_or_ean} ) != 13 ) { + $order_message .= "NAD+SU+" . $order->{san_or_ean} . "::31B'"; + $segment++; + } + else { + $order_message .= "NAD+SU+" . $order->{san_or_ean} . "::9'"; + $segment++; + } + + ### Library's internal ID for Vendor + $order_message .= "NAD+SU+" . $order->{provider_id} . "::92'"; + $segment++; + + ### Lineitems + foreach my $lineitem ( @{ $order->{lineitems} } ) { + use Rebus::EDI; + use Business::ISBN; + $linecount++; + my $note; + my $isbn; + if ( length( $lineitem->{isbn} ) == 10 + || substr( $lineitem->{isbn}, 0, 3 ) eq "978" + || index( $lineitem->{isbn}, "|" ) != -1 ) + { + $isbn = Rebus::EDI::cleanisbn( $lineitem->{isbn} ); + $isbn = Business::ISBN->new($isbn); + if ($isbn) { + if ( $isbn->is_valid ) { + $isbn = ( $isbn->as_isbn13 )->isbn; + } + else { + $isbn = "0"; + } + } + else { + $isbn = 0; + } + } + else { + $isbn = $lineitem->{isbn}; + } + + ### line number, isbn + $order_message .= "LIN+$linecount++" . $isbn . ":EN'"; + $segment++; + + ### isbn as main product identification + $order_message .= "PIA+5+" . $isbn . ":IB'"; + $segment++; + + ### title + $order_message .= + "IMD+L+050+:::" + . Rebus::EDI::string35escape( + Rebus::EDI::escape_reserved( $lineitem->{title} ) ) + . "'"; + $segment++; + + ### author + $order_message .= + "IMD+L+009+:::" + . Rebus::EDI::string35escape( + Rebus::EDI::escape_reserved( $lineitem->{author} ) ) + . "'"; + $segment++; + + ### publisher + $order_message .= + "IMD+L+109+:::" + . Rebus::EDI::string35escape( + Rebus::EDI::escape_reserved( $lineitem->{publisher} ) ) + . "'"; + $segment++; + + ### date of publication + $order_message .= "IMD+L+170+:::" + . Rebus::EDI::escape_reserved( $lineitem->{year} ) . "'"; + $segment++; + + ### binding + $order_message .= "IMD+L+220+:::" + . Rebus::EDI::escape_reserved( $lineitem->{binding} ) . "'"; + $segment++; + + ### quantity + $order_message .= "QTY+21:" + . Rebus::EDI::escape_reserved( $lineitem->{quantity} ) . "'"; + $segment++; + + ### copies + my $copyno = 0; + foreach my $copy ( @{ $lineitem->{copies} } ) { + my $gir_cnt = 0; + $copyno++; + $segment++; + + ### copy number + $order_message .= "GIR+" . sprintf( "%03d", $copyno ); + + ### quantity + $order_message .= "+1:LQT"; + $gir_cnt++; + + ### Library branchcode + $order_message .= "+" . $copy->{llo} . ":LLO"; + $gir_cnt++; + + ### Fund code + $order_message .= "+" . $copy->{lfn} . ":LFN"; + $gir_cnt++; + + ### call number + if ( $copy->{lcl} ) { + $order_message .= "+" . $copy->{lcl} . ":LCL"; + $gir_cnt++; + } + + ### copy location + if ( $copy->{lsq} ) { + $order_message .= "+" + . Rebus::EDI::string35escape( + Rebus::EDI::escape_reserved( $copy->{lsq} ) ) + . ":LSQ"; + $gir_cnt++; + } + + ### circ modifier + if ( $gir_cnt >= 5 ) { + $order_message .= "'GIR+" + . sprintf( "%03d", $copyno ) . "+" + . $copy->{lst} . ":LST"; + } + else { + $order_message .= "+" . $copy->{lst} . ":LST"; + } + + ### close GIR segment + $order_message .= "'"; + + $note = $copy->{note}; + } + + ### Freetext item note + if ($note) { + $order_message .= "FTX+LIN+++:::$note'"; + $segment++; + } + + ### price + if ( $lineitem->{price} ) { + $order_message .= "PRI+AAB:" . $lineitem->{price} . "'"; + $segment++; + } + + ### currency + $order_message .= "CUX+2:" . $lineitem->{currency} . ":9'"; + $segment++; + + ### Local order number + $order_message .= "RFF+LI:" . $lineitem->{rff} . "'"; + $segment++; + + ### Quote reference (if in response to quote) + if ( $order->{quote_or_order} eq 'q' ) { + $order_message .= "RFF+QLI:" . $lineitem->{qli} . "'"; + $segment++; + } + } + ### summary section header and number of lineitems contained in message + $order_message .= "UNS+S'"; + $segment++; + + ### Number of lineitems contained in the message_count + $order_message .= "CNT+2:$linecount'"; + $segment++; + + ### number of segments in the message (+1 to include the UNT segment itself) and reference number from UNH segment + $segment++; + $order_message .= "UNT+$segment+" . $ref . "'"; + + ### Exchange reference number from UNB segment + $order_message .= "UNZ+1+" . $exchange . "'"; + return $order_message; +} + +sub post_process_quote_file { + my ( $self, $remote_file, $ftp_account ) = @_; + + ### connect to vendor ftp account + my $filename = substr( $remote_file, rindex( $remote_file, '/' ) + 1 ); + use Net::FTP::File; + my $ftp = Net::FTP->new( $ftp_account->{host}, Timeout => 10 ) + or die "Couldn't connect"; + $ftp->login( $ftp_account->{username}, $ftp_account->{password} ) + or die "Couldn't log in"; + $ftp->cwd( $ftp_account->{in_dir} ) or die "Couldn't change directory"; + + ### move file to another directory +#my $new_dir='processed'; +#my $new_file=$new_dir."/".$filename; +#$ftp->copy($filename, $new_file) or die "Couldn't move remote file to $new_file "; +#$ftp->delete($filename); +#$ftp->quit; + + ### rename file + my $rext = '.EEQ'; + my $qext = '.CEQ'; + $filename =~ s/$qext/$rext/g; + $ftp->rename( $remote_file, $filename ) + or die "Couldn't rename remote file"; +} + +1; --- a/acqui/basket.pl +++ a/acqui/basket.pl @@ -35,6 +35,7 @@ use C4::Members qw/GetMember/; #needed for permissions checking for changing ba use C4::Items; use C4::Suggestions; use Date::Calc qw/Add_Delta_Days/; +use C4::Edifact; =head1 NAME @@ -66,6 +67,7 @@ the supplier this script have to display the basket. my $query = new CGI; my $basketno = $query->param('basketno'); +my $ean = $query->param('ean'); my $booksellerid = $query->param('booksellerid'); my ( $template, $loggedinuser, $cookie ) = get_template_and_user( @@ -86,12 +88,21 @@ my $basket = GetBasket($basketno); # if no booksellerid in parameter, get it from basket # warn "=>".$basket->{booksellerid}; $booksellerid = $basket->{booksellerid} unless $booksellerid; +my $ediaccount = CheckVendorFTPAccountExists($booksellerid); +$template->param(ediaccount=>$ediaccount); my ($bookseller) = GetBookSellerFromId($booksellerid); my $op = $query->param('op'); if (!defined $op) { $op = q{}; } +if ( $op eq 'ediorder') { + use Rebus::EDI; + my $edi=Rebus::EDI->new(); + $edi->send_orders($basketno,$ean); + $template->param(edifile => 1); +} + my $confirm_pref= C4::Context->preference("BasketConfirmations") || '1'; $template->param( skip_confirm_reopen => 1) if $confirm_pref eq '2'; --- a/acqui/edi_ean.pl +++ a/acqui/edi_ean.pl @@ -0,0 +1,56 @@ +#!/usr/bin/perl + +# Copyright 2012 Mark Gavillet & PTFS Europe Ltd +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; + +use C4::Auth; +use C4::Koha; +use C4::Output; +use CGI; +use C4::Edifact; + +my $eans = GetEDIfactEANs(); +my $total_eans = scalar( @{$eans} ); +my $query = new CGI; +my $basketno = $query->param('basketno'); +my $ean; + +if ( $total_eans == 1 ) { + $ean = $eans->[0]->{ean}; + print $query->redirect( + "/cgi-bin/koha/acqui/basket.pl?basketno=$basketno&op=ediorder&ean=$ean" + ); +} +else { + my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "acqui/edi_ean.tt", + query => $query, + type => "intranet", + authnotrequired => 0, + flagsrequired => { acquisition => 'order_manage' }, + debug => 1, + } + ); + $template->param( eans => $eans ); + $template->param( basketno => $basketno ); + + output_html_with_http_headers $query, $cookie, $template->output; +} --- a/admin/edi-accounts.pl +++ a/admin/edi-accounts.pl @@ -0,0 +1,78 @@ +#!/usr/bin/perl + +# Copyright 2011 Mark Gavillet & PTFS Europe Ltd +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; +use CGI; +use C4::Auth; +use C4::Output; +use C4::Edifact; + +use vars qw($debug); + +BEGIN { + $debug = $ENV{DEBUG} || 0; +} + +my $input = CGI->new(); + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "admin/edi-accounts.tmpl", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { borrowers => 1 }, + debug => ($debug) ? 1 : 0, + } +); + +my $op = $input->param('op'); +$template->param( op => $op ); + +if ( $op eq "delsubmit" ) { + my $del = C4::Edifact::DeleteEDIDetails( $input->param('id') ); + $template->param( opdelsubmit => 1 ); +} + +if ( $op eq "addsubmit" ) { + CreateEDIDetails( + $input->param('provider'), $input->param('description'), + $input->param('host'), $input->param('user'), + $input->param('pass'), $input->param('path'), + $input->param('in_dir'), $input->param('san') + ); + $template->param( opaddsubmit => 1 ); +} + +if ( $op eq "editsubmit" ) { + UpdateEDIDetails( + $input->param('editid'), $input->param('description'), + $input->param('host'), $input->param('user'), + $input->param('pass'), $input->param('provider'), + $input->param('path'), $input->param('in_dir'), + $input->param('san') + ); + $template->param( opeditsubmit => 1 ); +} + +my $ediaccounts = C4::Edifact::GetEDIAccounts; +$template->param( ediaccounts => $ediaccounts ); + +output_html_with_http_headers $input, $cookie, $template->output; --- a/admin/edi-edit.pl +++ a/admin/edi-edit.pl @@ -0,0 +1,80 @@ +#!/usr/bin/perl + +# Copyright 2011 Mark Gavillet & PTFS Europe Ltd +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; +use CGI; +use C4::Auth; +use C4::Output; +use C4::Edifact; + +use vars qw($debug); + +BEGIN { + $debug = $ENV{DEBUG} || 0; +} + +my $input = CGI->new(); + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "admin/edi-edit.tmpl", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { borrowers => 1 }, + debug => ($debug) ? 1 : 0, + } +); +my $vendorlist = C4::Edifact::GetVendorList; + +my $op = $input->param('op'); +$template->param( op => $op ); + +if ( $op eq "add" ) { + $template->param( opaddsubmit => "addsubmit" ); +} +if ( $op eq "edit" ) { + $template->param( opeditsubmit => "editsubmit" ); + my $edi_details = C4::Edifact::GetEDIAccountDetails( $input->param('id') ); + my $selectedprovider = $edi_details->{'provider'}; + foreach my $prov (@$vendorlist) { + $prov->{selected} = 'selected' + if $prov->{'id'} == $selectedprovider; + } + $template->param( + editid => $edi_details->{'id'}, + description => $edi_details->{'description'}, + host => $edi_details->{'host'}, + user => $edi_details->{'username'}, + pass => $edi_details->{'password'}, + provider => $edi_details->{'provider'}, + in_dir => $edi_details->{'in_dir'}, + san => $edi_details->{'san'} + ); +} +if ( $op eq "del" ) { + $template->param( opdelsubmit => "delsubmit" ); + $template->param( opdel => 1 ); + $template->param( id => $input->param('id') ); +} + +$template->param( vendorlist => $vendorlist ); + +output_html_with_http_headers $input, $cookie, $template->output; --- a/admin/edi_ean_accounts.pl +++ a/admin/edi_ean_accounts.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl + +# Copyright 2012 Mark Gavillet & PTFS Europe Ltd +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; +use CGI; +use C4::Auth; +use C4::Output; +use C4::Edifact; + +use vars qw($debug); + +BEGIN { + $debug = $ENV{DEBUG} || 0; +} + +my $input = CGI->new(); + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "admin/edi_ean_accounts.tt", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { borrowers => 1 }, + debug => ($debug) ? 1 : 0, + } +); + +my $op = $input->param('op'); +$template->param( op => $op ); + +if ( $op eq "delsubmit" ) { + my $del = C4::Edifact::delete_edi_ean( $input->param('branchcode'), + $input->param('ean') ); + $template->param( opdelsubmit => 1 ); +} + +if ( $op eq "addsubmit" ) { + create_edi_ean( $input->param('branchcode'), $input->param('ean') ); + $template->param( opaddsubmit => 1 ); +} + +if ( $op eq "editsubmit" ) { + update_edi_ean( + $input->param('branchcode'), $input->param('ean'), + $input->param('oldbranchcode'), $input->param('oldean') + ); + $template->param( opeditsubmit => 1 ); +} + +my $eans = C4::Edifact::GetEDIfactEANs; +$template->param( eans => $eans ); + +output_html_with_http_headers $input, $cookie, $template->output; --- a/admin/edi_ean_edit.pl +++ a/admin/edi_ean_edit.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl + +# Copyright 2012 Mark Gavillet & PTFS Europe Ltd +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; +use CGI; +use C4::Auth; +use C4::Output; +use C4::Edifact; + +use vars qw($debug); + +BEGIN { + $debug = $ENV{DEBUG} || 0; +} + +my $input = CGI->new(); + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "admin/edi_ean_edit.tt", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { borrowers => 1 }, + debug => ($debug) ? 1 : 0, + } +); +my $branchlist = C4::Edifact::GetBranchList; + +my $op = $input->param('op'); +$template->param( op => $op ); + +if ( $op eq "add" ) { + $template->param( opaddsubmit => "addsubmit" ); +} +if ( $op eq "edit" ) { + $template->param( opeditsubmit => "editsubmit" ); + + $template->param( + ean => $input->param('ean'), + selectedbranch => $input->param('branchcode'), + branchcode => $input->param('branchcode') + ); +} +if ( $op eq "del" ) { + $template->param( opdelsubmit => "delsubmit" ); + $template->param( opdel => 1 ); + $template->param( ean => $input->param('ean') ); + $template->param( branchcode => $input->param('branchcode') ); +} + +$template->param( branchlist => $branchlist ); + +output_html_with_http_headers $input, $cookie, $template->output; --- a/installer/data/mysql/de-DE/mandatory/userpermissions.sql +++ a/installer/data/mysql/de-DE/mandatory/userpermissions.sql @@ -39,6 +39,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES (13, 'moderate_tags', 'Von Benutzern vergebene Tags moderieren'), (13, 'rotating_collections', 'Wandernde Sammlungen verwalten'), (13, 'upload_local_cover_images', 'Eigene Coverbilder hochladen'), + (13, 'edi_manage', 'Manage EDIFACT transmissions'), (15, 'check_expiration', 'Ablauf eines Abonnements prüfen'), (15, 'claim_serials', 'Fehlende Hefte reklamieren'), (15, 'create_subscription', 'Neues Abonnement anlegen'), --- a/installer/data/mysql/en/mandatory/userpermissions.sql +++ a/installer/data/mysql/en/mandatory/userpermissions.sql @@ -39,6 +39,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES (13, 'moderate_tags', 'Moderate patron tags'), (13, 'rotating_collections', 'Manage rotating collections'), (13, 'upload_local_cover_images', 'Upload local cover images'), + (13, 'edi_manage', 'Manage EDIFACT transmissions'), (15, 'check_expiration', 'Check the expiration of a serial'), (15, 'claim_serials', 'Claim missing serials'), (15, 'create_subscription', 'Create a new subscription'), --- a/installer/data/mysql/es-ES/mandatory/userpermissions.sql +++ a/installer/data/mysql/es-ES/mandatory/userpermissions.sql @@ -39,6 +39,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES (13, 'moderate_tags', 'Moderate patron tags'), (13, 'rotating_collections', 'Manage rotating collections'), (13, 'upload_local_cover_images', 'Upload local cover images'), + (13, 'edi_manage', 'Manage EDIFACT transmissions'), (15, 'check_expiration', 'Check the expiration of a serial'), (15, 'claim_serials', 'Claim missing serials'), (15, 'create_subscription', 'Create a new subscription'), --- a/installer/data/mysql/fr-FR/1-Obligatoire/userpermissions.sql +++ a/installer/data/mysql/fr-FR/1-Obligatoire/userpermissions.sql @@ -39,6 +39,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES (13, 'items_batchmod', 'Modifier les exemplaires par lot'), (13, 'items_batchdel', 'Supprimer les exemplaires par lot'), (13, 'upload_local_cover_images', 'Téléchargement des images de couverture'), + (13, 'edi_manage', 'Manage EDIFACT transmissions'), (15, 'check_expiration', 'Contrôler l''expiration d''un périodique'), (15, 'claim_serials', 'Réclamer les périodiques manquants'), (15, 'create_subscription', 'Créer de nouveaux abonnements'), --- a/installer/data/mysql/it-IT/necessari/userpermissions.sql +++ a/installer/data/mysql/it-IT/necessari/userpermissions.sql @@ -41,6 +41,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES (13, 'moderate_tags', 'Modera i tag inseriti dagli utenti'), (13, 'rotating_collections', 'Gestisci le collezioni circolanti (rotating collections)'), (13, 'upload_local_cover_images', 'Carica copertine in locale'), + (13, 'edi_manage', 'Manage EDIFACT transmissions'), (15, 'check_expiration', 'Controlla la scadenza di una risora in continuazione'), (15, 'claim_serials', 'Richiedi i fascicoli non arrivati'), (15, 'create_subscription', 'Crea un nuovo abbonamento'), --- a/installer/data/mysql/kohastructure.sql +++ a/installer/data/mysql/kohastructure.sql @@ -2846,6 +2846,51 @@ CREATE TABLE `quotes` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +-- +-- vendor_edi_accounts +-- + +DROP TABLE IF EXISTS vendor_edi_accounts; +CREATE TABLE vendor_edi_accounts ( + id int(11) NOT NULL auto_increment, + description text NOT NULL, + host text,username text, + password text, + last_activity date default NULL, + provider int(11) default NULL, + in_dir text, + san varchar(20) default NULL, + PRIMARY KEY (id) + +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- edifact_messages +-- + +DROP TABLE IF EXISTS edifact_messages; +CREATE TABLE edifact_messages ( + key int(11) NOT NULL auto_increment, + message_type text NOT NULL, + date_sent date default NULL, + provider int(11) default NULL, + status text, + basketno int(11) NOT NULL default '0', + edi LONGTEXT, + remote_file TEXT, + PRIMARY KEY (key) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +-- +-- edifact_ean +-- + +DROP TABLE IF EXISTS edifact_ean; +CREATE TABLE edifact_ean ( + branchcode varchar(10) NOT NULL default '', + ean varchar(15) NOT NULL default '', + UNIQUE KEY `edifact_ean_branchcode` (branchcode) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; --- a/installer/data/mysql/nb-NO/1-Obligatorisk/userpermissions.sql +++ a/installer/data/mysql/nb-NO/1-Obligatorisk/userpermissions.sql @@ -60,6 +60,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES (13, 'moderate_tags', 'Behandle tagger fra lånere'), (13, 'rotating_collections', 'Administrere roterende samlinger'), (13, 'upload_local_cover_images', 'Laste opp lokale omslagsbilder'), + (13, 'edi_manage', 'Manage EDIFACT transmissions'), (15, 'check_expiration', 'Sjekke utløpsdato for et periodikum'), (15, 'claim_serials', 'Purre manglende tidsskrifthefter'), (15, 'create_subscription', 'Opprette abonnementer'), --- a/installer/data/mysql/pl-PL/mandatory/userpermissions.sql +++ a/installer/data/mysql/pl-PL/mandatory/userpermissions.sql @@ -40,6 +40,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES (16, 'execute_reports', 'Execute SQL reports'), (13, 'rotating_collections', 'Manage rotating collections'), (13, 'upload_local_cover_images', 'Upload local cover images'), + (13, 'edi_manage', 'Manage EDIFACT transmissions'), (15, 'check_expiration', 'Check the expiration of a serial'), (15, 'claim_serials', 'Claim missing serials'), (15, 'create_subscription', 'Create a new subscription'), --- a/installer/data/mysql/ru-RU/mandatory/permissions_and_user_flags.sql +++ a/installer/data/mysql/ru-RU/mandatory/permissions_and_user_flags.sql @@ -63,6 +63,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES (13, 'moderate_tags', 'Moderate patron tags'), (13, 'rotating_collections', 'Manage rotating collections'), (13, 'upload_local_cover_images', 'Upload local cover images'), + (13, 'edi_manage', 'Manage EDIFACT transmissions'), (15, 'check_expiration', 'Check the expiration of a serial'), (15, 'claim_serials', 'Claim missing serials'), (15, 'create_subscription', 'Create a new subscription'), --- a/installer/data/mysql/uk-UA/mandatory/permissions_and_user_flags.sql +++ a/installer/data/mysql/uk-UA/mandatory/permissions_and_user_flags.sql @@ -63,6 +63,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES (13, 'moderate_tags', 'Moderate patron tags'), (13, 'rotating_collections', 'Manage rotating collections'), (13, 'upload_local_cover_images', 'Upload local cover images'), + (13, 'edi_manage', 'Manage EDIFACT transmissions'), (15, 'check_expiration', 'Check the expiration of a serial'), (15, 'claim_serials', 'Claim missing serials'), (15, 'create_subscription', 'Create a new subscription'), --- a/installer/data/mysql/updatedatabase.pl +++ a/installer/data/mysql/updatedatabase.pl @@ -5370,6 +5370,23 @@ if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) { } +$DBversion = "3.09.00.XXX"; +if (C4::Context->preference("Version") < TransformToNum($DBversion)) { + $dbh->do( + "CREATE TABLE IF NOT EXISTS `vendor_edi_accounts` (`id` int(11) NOT NULL auto_increment,`description` text NOT NULL,`host` text,`username` text,`password` text,`last_activity` date default NULL,`provider` int(11) default NULL,`in_dir` text,`san` varchar(20) default NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8" + ); + $dbh->do( + "CREATE TABLE IF NOT EXISTS `edifact_messages` (`key` int(11) NOT NULL auto_increment,`message_type` text NOT NULL,`date_sent` date default NULL,`provider` int(11) default NULL,`status` text,`basketno` int(11) NOT NULL default '0', edi LONGTEXT, remote_file TEXT, PRIMARY KEY (`key`)) ENGINE=InnoDB DEFAULT CHARSET=utf8" + $dbh->do( + "CREATE TABLE IF NOT EXISTS `edifact_ean` (`branchcode` varchar(10) NOT NULL default '',`ean` varchar(15) NOT NULL default '',UNIQUE KEY `edifact_ean_branchcode` (`branchcode`)) ENGINE=InnoDB DEFAULT CHARSET=utf8" + ); + $dbh->do( + "insert into permissions (module_bit, code, description) values (13, 'edi_manage', 'Manage EDIFACT transmissions')" + ); + print "Upgrade to $DBversion done (Bug xxxx: Edifact quote and order processing.)\n"; + SetVersion($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) --- a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc +++ a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc @@ -62,6 +62,8 @@ [% IF ( NoZebra ) %]
  • Stop words
  • [% END %]
  • Z39.50 client targets
  • +
  • EDI Accounts
  • +
  • EDI EANs
  • --- a/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc +++ a/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc @@ -98,4 +98,7 @@ [% IF ( CAN_user_tools_edit_quotes ) %]
  • Quote editor
  • [% END %] + [% IF ( CAN_user_tools_edi_manage ) %] +
  • EDIfact messages
  • + [% END %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/basket.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/basket.tt @@ -120,6 +120,7 @@ new YAHOO.widget.Button("basketheadbutton"); new YAHOO.widget.Button("exportbutton"); new YAHOO.widget.Button("delbasketbutton"); + new YAHOO.widget.Button("ediorderbutton"); } //]]> @@ -134,6 +135,9 @@
  • Close this basket
  • [% END %]
  • Export this basket as CSV
  • + [% IF ( ediaccount ) %] +
  • EDIfact order
  • + [% END %] @@ -159,7 +163,9 @@ [% END %] [% END %] [% END %] - + [% IF ( edifile ) %] +
    The EDIfact order was successfully transferred to the bookseller
    + [% END %] [% IF ( NO_BOOKSELLER ) %]

    Vendor not found

    [% ELSE %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/edi_ean.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/edi_ean.tt @@ -0,0 +1,40 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Acquisitions › Basket ([% basketno %]) +[% INCLUDE 'doc-head-close.inc' %] + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'acquisitions-search.inc' %] + + + +
    + +
    +
    +
    + +

    Identify the branch submitting the EDI order

    +
    +

    +

    +

    Ordering branch: + + + +

    +
    +

    + +
    +
    +
    +[% INCLUDE 'acquisitions-menu.inc' %] +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt @@ -99,6 +99,10 @@
    Printers (UNIX paths).
    -->
    Z39.50 client targets
    Define which servers to query for MARC data in the integrated Z39.50 client.
    +
    EDI Accounts
    +
    Manage vendor EDI accounts
    +
    EDI EANs
    +
    Manage Branch EDI EANs
    --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/edi-accounts.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/edi-accounts.tt @@ -0,0 +1,46 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Administration - EDI Accounts +[% INCLUDE 'doc-head-close.inc' %] + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + + +
    +
    +
    +
    Vendor EDI Accounts + [% IF ( ediaccounts ) %] +
    + [% IF ( opdelsubmit ) %] +
    The account was successfully deleted
    + [% END %] + [% IF ( opaddsubmit ) %] +
    The account was successfully added
    + [% END %] + [% IF ( opeditsubmit ) %] +
    The account was successfully updated
    + [% END %] + + + + [% FOREACH account IN ediaccounts %] + + [% END %] +
    IDVendorDescriptionLast activityActions
    [% account.id %][% account.vendor %][% account.description %][% account.last_activity %]Edit | Delete
    +
    + [% ELSE %] +

    You currently do not have any Vendor EDI Accounts. To add a new account click here.

    + [% END %] +
    +
    +
    + [% INCLUDE 'admin-menu.inc' %] +
    +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/edi-edit.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/edi-edit.tt @@ -0,0 +1,102 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Administration - EDI Accounts +[% INCLUDE 'doc-head-close.inc' %] + +[% INCLUDE 'calendar.inc' %] + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + +
    +
    +
    +
    +

    Vendor EDI Accounts

    +
    You must complete all required fields
    +
    + + + + [% IF ( opdel ) %] +

    Are you sure you want to delete this account?

    +

    YES | NO

    + [% ELSE %] +
    + EDI Account details +
      +
    1. + + +
    2. +
    3. + + +
    4. +
    5. + + +
    6. +
    7. + + +
    8. +
    9. + + +
    10. +
    11. + + +
    12. +
    13. + + +
    14. +
    +
    +
    + + Cancel +
    + [% END %] +
    + +
    +
    +
    + [% INCLUDE 'admin-menu.inc' %] +
    +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/edi_ean_accounts.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/edi_ean_accounts.tt @@ -0,0 +1,46 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Administration - EDI EANs +[% INCLUDE 'doc-head-close.inc' %] + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + + +
    +
    +
    +
    Branch EDI EANs + [% IF ( eans ) %] +
    + [% IF ( opdelsubmit ) %] +
    The EAN was successfully deleted
    + [% END %] + [% IF ( opaddsubmit ) %] +
    The EAN was successfully added
    + [% END %] + [% IF ( opeditsubmit ) %] +
    The EAN was successfully updated
    + [% END %] + + + + [% FOREACH ean IN eans %] + + [% END %] +
    BranchEANActions
    [% ean.branchname %][% ean.ean %]Edit | Delete
    +
    + [% ELSE %] +

    You currently do not have any EANs. To add a new EAN click here.

    + [% END %] +
    +
    +
    + [% INCLUDE 'admin-menu.inc' %] +
    +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/edi_ean_edit.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/edi_ean_edit.tt @@ -0,0 +1,74 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Administration - EDI EANs +[% INCLUDE 'doc-head-close.inc' %] + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + +
    +
    +
    +
    +

    Branch EDI EANs

    +
    You must complete all required fields
    +
    + + + [% IF ( opdel ) %] +

    Are you sure you want to delete this account?

    +

    YES | NO

    + [% ELSE %] +
    + EAN details +
      +
    1. + + +
    2. +
    3. + + +
    4. + + +
    +
    +
    + + Cancel +
    + [% END %] +
    + +
    +
    +
    + [% INCLUDE 'admin-menu.inc' %] +
    +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/edi.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/edi.tt @@ -0,0 +1,54 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Tools › EDIfact messages +[% INCLUDE 'doc-head-close.inc' %] + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + + +
    +
    +
    +
    EDIfact messages + [% IF ( messagelist ) %] +
    + [% IF ( opdelsubmit ) %] +
    The account was successfully deleted
    + [% END %] + [% IF ( opaddsubmit ) %] +
    The account was successfully added
    + [% END %] + [% IF ( opeditsubmit ) %] +
    The account was successfully updated
    + [% END %] + + + [% FOREACH message IN messagelist %] + + [% END %] +
    DateMessage typeProviderStatusBasket
    [% message.date_sent %][% message.message_type %][% message.providername %][% message.status %][% IF ( message.basketno ) %]View basket[% END %]
    +
    + + [% ELSE %] +

    There are currently no EDIfact messages to display.

    + [% END %] +
    +
    +
    + [% INCLUDE 'tools-menu.inc' %] +
    +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt @@ -96,6 +96,10 @@
    Edit quotes for QOTD feature
    Quote editor for Quote-of-the-day feature in OPAC
    [% END %] + [% IF ( CAN_user_tools_edi_manage ) %] +
    EDIfact messages
    +
    Manage EDIfact transmissions
    + [% END %] --- a/misc/cronjobs/clean_edifiles.pl +++ a/misc/cronjobs/clean_edifiles.pl @@ -0,0 +1,41 @@ +#!/usr/bin/perl + +# Copyright 2011 Mark Gavillet & PTFS Europe Ltd +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; + +my $edidir = "$ENV{'PERL5LIB'}/misc/edi_files"; +opendir( my $dh, $edidir ); +my @files = readdir($dh); +close $dh; + +foreach my $file (@files) { + my $now = time; + my @stat = stat("$edidir/$file"); + if ( + $stat[9] < ( $now - 2592000 ) + && ( ( index lc($file), '.ceq' ) > -1 + || ( index lc($file), '.cep' ) > -1 ) + ) + { + print "Deleting file $edidir/$file..."; + unlink("$edidir/$file"); + print "Done.\n"; + } +} --- a/misc/cronjobs/edi_quote_cron.pl +++ a/misc/cronjobs/edi_quote_cron.pl @@ -0,0 +1,12 @@ +#!/usr/bin/perl + +use warnings; +use strict; + +use Data::Dumper; + +use Rebus::EDI; + +my $edi = Rebus::EDI->new(); + +my $result = $edi->retrieve_quotes; --- a/misc/cronjobs/rotate_edi_logs.pl +++ a/misc/cronjobs/rotate_edi_logs.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my $edidir = "$ENV{'PERL5LIB'}/misc/edi_files/"; # koha + +#my $edidir="/tmp/"; # evergreen + +my @logfiles = ( '$edidir/edi_ftp.log', '$edidir/edi_quote_error.log' ); +my $rotate_size = 10485760; # 10MB + +my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(time); +my $currdate = sprintf "%4d%02d%02d", $year + 1900, $mon + 1, $mday; + +foreach my $file (@logfiles) { + my $current_size = -s $file; + print "$current_size\n"; + if ( $current_size > $rotate_size ) { + rename( $file, $file . "." . $currdate ); + } +} --- a/misc/edi_files/ptfs-europe-koha-community.CEQ +++ a/misc/edi_files/ptfs-europe-koha-community.CEQ @@ -0,0 +1, @@ +UNA:+.? 'UNB+UNOC:2+5011234567890:14+5099876543210:31B+111205:1135+11775066594509++QUOTES'UNH+MG0011+QUOTES:D:96A:UN:EAN008'BGM+31C::28+MG0011+9'DTM+137:20111205:102'NAD+BY+5099876543210:31B'NAD+SU+5011234567890:14'LIN+1++9781844676828:EN'IMD+L+010+:::Baudrillard'IMD+L+011+:::Jean'IMD+L+050+:::America'IMD+L+109+:::Verso Books'IMD+L+120+:::Verso Books'IMD+L+170+:::2010'IMD+L+220+:::PBK'IMD+L+230+:::123.456 BAU'QTY+1:2'GIR+001+BRANCH1:LLO+FUND1:LFN+BKREF:LST+REFERENCE:LSQ'GIR+002+BRANCH2:LLO+FUND1:LFN+BK1WK:LST+STORE:LSQ'FTX+LNO++2:10B:28+Note 1'PRI+AAB:5.73'CUX+2:GBP:9'RFF+QLI:MG0011/001'LIN+2++9781846554070:EN'IMD+L+010+:::Murakami'IMD+L+011+:::Haruki'IMD+L+050+:::1Q84'IMD+L+109+:::Harvill Secker'IMD+L+120+:::Harvill Secker'IMD+L+170+:::2011'IMD+L+220+:::HBK'IMD+L+230+:::123.456 MUR'QTY+1:1'GIR+001+BRANCH2:LLO+FUND2:LFN+BK4WK:LST+SHELVES:LSQ'FTX+LNO++2:10B:28+Books 1 and 2'PRI+AAB:10.23'CUX+2:GBP:9'RFF+QLI:MG0011/002'UNS+S'CNT+2:1'UNT+39+MG0011'UNZ+1+11775066594509' --- a/tools/edi.pl +++ a/tools/edi.pl @@ -0,0 +1,50 @@ +#!/usr/bin/perl + +# Copyright 2011 Mark Gavillet & PTFS Europe Ltd +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; +use CGI; +use C4::Auth; +use C4::Output; +use C4::Edifact; + +use vars qw($debug); + +BEGIN { + $debug = $ENV{DEBUG} || 0; +} + +my $input = CGI->new(); + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "tools/edi.tmpl", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { borrowers => 1 }, + debug => ($debug) ? 1 : 0, + } +); + +my $messagelist = C4::Edifact::GetEDIfactMessageList(); + +$template->param( messagelist => $messagelist ); + +output_html_with_http_headers $input, $cookie, $template->output; --