Bug 7736: Support Ordering via Edifact EDI messages
Add support for processing incoming Edifact Quotes, Invoices
and order responses and generating and transmission of
Edifact Orders.
Basic workflow is that an incoming quote generates an aquisition
basket in Koha, with each line corresponding to an order record
The user can then generate an edifact order from this (or another)
basket, which is transferred to the vendor's site
The supplier generates an invoice on despatch and this will
result in corresponding invoices being generated in Koha
The orderlines on the invoice are receipted automatically.
We also support order response messages. This may include
simple order acknowledgements, supplier reports/amendments
on availability. Cancellation messages cause the koha order
to be cancelled, other messages are recorded against the order
Which messages are to be supported/processed is specifiable on a
vendor by vendor basis via the admin screens
You can also specify auto order i.e. to generate orders from quotes
without user intervention - This reflects existing
workflows where most work is done on the suppliers website
then generating a dummy quote
Received messages are stored in the edifact_messages table
and the original can be viewed via the online
Database changes are in installer/data/mysql/atomicchanges/edifact.sql
Note new perl dependencies:
Net::SFTP:Foreign
Text::Unidecode
Signed-off-by: Paul Johnson <p.johnson@staffs.ac.uk>
Signed-off-by: Sally Healey <sally.healey@cheshiresharedservices.gov.uk>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Brendan A Gallagher <brendan@bywatersolutions.com>
This commit is contained in:
parent
9658085d1e
commit
e2e9916348
|
|
@ -2476,6 +2476,10 @@ sub GetInvoices {
|
|||
push @bind_strs, " borrowers.branchcode = ? ";
|
||||
push @bind_args, $args{branchcode};
|
||||
}
|
||||
if($args{message_id}) {
|
||||
push @bind_strs, " aqinvoices.message_id = ? ";
|
||||
push @bind_args, $args{message_id};
|
||||
}
|
||||
|
||||
$query .= " WHERE " . join(" AND ", @bind_strs) if @bind_strs;
|
||||
$query .= " GROUP BY aqinvoices.invoiceid ";
|
||||
|
|
@ -2600,7 +2604,7 @@ sub AddInvoice {
|
|||
return unless(%invoice and $invoice{invoicenumber});
|
||||
|
||||
my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
|
||||
closedate shipmentcost shipmentcost_budgetid);
|
||||
closedate shipmentcost shipmentcost_budgetid message_id);
|
||||
|
||||
my @set_strs;
|
||||
my @set_args;
|
||||
|
|
|
|||
|
|
@ -782,6 +782,16 @@ our $PERL_DEPS = {
|
|||
'required' => '0',
|
||||
'min_ver' => '0.56',
|
||||
},
|
||||
'Net::SFTP::Foreign' => {
|
||||
'usage' => 'Edifact',
|
||||
'required' => '0',
|
||||
'min_ver' => '1.73',
|
||||
},
|
||||
'Text::Unidecode' => {
|
||||
'usage' => 'Edifact',
|
||||
'required' => '0',
|
||||
'min_ver' => '0.04',
|
||||
},
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,337 @@
|
|||
package Koha::Edifact;
|
||||
|
||||
# Copyright 2014,2015 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 3 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, see <http://www.gnu.org/licenses>.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use File::Slurp;
|
||||
use Carp;
|
||||
use Encode qw( from_to );
|
||||
use Koha::Edifact::Segment;
|
||||
use Koha::Edifact::Message;
|
||||
|
||||
my $separator = {
|
||||
component => q{\:},
|
||||
data => q{\+},
|
||||
decimal => q{.},
|
||||
release => q{\?},
|
||||
reserved => q{ },
|
||||
segment => q{\'},
|
||||
};
|
||||
|
||||
sub new {
|
||||
my ( $class, $param_hashref ) = @_;
|
||||
my $transmission;
|
||||
my $self = ();
|
||||
|
||||
if ( $param_hashref->{filename} ) {
|
||||
if ( $param_hashref->{transmission} ) {
|
||||
carp
|
||||
"Cannot instantiate $class : both filename and transmission passed";
|
||||
return;
|
||||
}
|
||||
$transmission = read_file( $param_hashref->{filename} );
|
||||
}
|
||||
else {
|
||||
$transmission = $param_hashref->{transmission};
|
||||
}
|
||||
$self->{transmission} = _init($transmission);
|
||||
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub interchange_header {
|
||||
my ( $self, $field ) = @_;
|
||||
|
||||
my %element = (
|
||||
sender => 1,
|
||||
recipient => 2,
|
||||
datetime => 3,
|
||||
interchange_control_reference => 4,
|
||||
application_reference => 6,
|
||||
);
|
||||
if ( !exists $element{$field} ) {
|
||||
carp "No interchange header field $field available";
|
||||
return;
|
||||
}
|
||||
my $data = $self->{transmission}->[0]->elem( $element{$field} );
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub interchange_trailer {
|
||||
my ( $self, $field ) = @_;
|
||||
my $trailer = $self->{transmission}->[-1];
|
||||
if ( $field eq 'interchange_control_count' ) {
|
||||
return $trailer->elem(0);
|
||||
}
|
||||
elsif ( $field eq 'interchange_control_reference' ) {
|
||||
return $trailer->elem(1);
|
||||
}
|
||||
carp "Trailer field $field not recognized";
|
||||
return;
|
||||
}
|
||||
|
||||
sub new_data_iterator {
|
||||
my $self = shift;
|
||||
my $offset = 0;
|
||||
while ( $self->{transmission}->[$offset]->tag() ne 'UNH' ) {
|
||||
++$offset;
|
||||
if ( $offset == @{ $self->{transmission} } ) {
|
||||
carp 'Cannot find message start';
|
||||
return;
|
||||
}
|
||||
}
|
||||
$self->{data_iterator} = $offset;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub next_segment {
|
||||
my $self = shift;
|
||||
if ( defined $self->{data_iterator} ) {
|
||||
my $seg = $self->{transmission}->[ $self->{data_iterator} ];
|
||||
if ( $seg->tag eq 'UNH' ) {
|
||||
|
||||
$self->{msg_type} = $seg->elem( 1, 0 );
|
||||
}
|
||||
elsif ( $seg->tag eq 'LIN' ) {
|
||||
$self->{msg_type} = 'detail';
|
||||
}
|
||||
|
||||
if ( $seg->tag ne 'UNZ' ) {
|
||||
$self->{data_iterator}++;
|
||||
}
|
||||
else {
|
||||
$self->{data_iterator} = undef;
|
||||
}
|
||||
return $seg;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# for debugging return whole transmission
|
||||
sub get_transmission {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{transmission};
|
||||
}
|
||||
|
||||
sub message_type {
|
||||
my $self = shift;
|
||||
return $self->{msg_type};
|
||||
}
|
||||
|
||||
sub _init {
|
||||
my $msg = shift;
|
||||
if ( !$msg ) {
|
||||
return;
|
||||
}
|
||||
if ( $msg =~ s/^UNA(.{6})// ) {
|
||||
if ( service_string_advice($1) ) {
|
||||
return segmentize($msg);
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
my $s = substr $msg, 10;
|
||||
croak "File does not start with a Service string advice :$s";
|
||||
}
|
||||
}
|
||||
|
||||
# return an array of Message objects
|
||||
sub message_array {
|
||||
my $self = shift;
|
||||
|
||||
# return an array of array_refs 1 ref to a message
|
||||
my $msg_arr = [];
|
||||
my $msg = [];
|
||||
my $in_msg = 0;
|
||||
foreach my $seg ( @{ $self->{transmission} } ) {
|
||||
if ( $seg->tag eq 'UNH' ) {
|
||||
$in_msg = 1;
|
||||
push @{$msg}, $seg;
|
||||
}
|
||||
elsif ( $seg->tag eq 'UNT' ) {
|
||||
$in_msg = 0;
|
||||
if ( @{$msg} ) {
|
||||
push @{$msg_arr}, Koha::Edifact::Message->new($msg);
|
||||
$msg = [];
|
||||
}
|
||||
}
|
||||
elsif ($in_msg) {
|
||||
push @{$msg}, $seg;
|
||||
}
|
||||
}
|
||||
return $msg_arr;
|
||||
}
|
||||
|
||||
#
|
||||
# internal parsing routines used in _init
|
||||
#
|
||||
sub service_string_advice {
|
||||
my $ssa = shift;
|
||||
|
||||
# At present this just validates that the ssa
|
||||
# is standard Edifact
|
||||
# TBD reset the seps if non standard
|
||||
if ( $ssa ne q{:+.? '} ) {
|
||||
carp " Non standard Service String Advice [$ssa]";
|
||||
return;
|
||||
}
|
||||
|
||||
# else use default separators
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub segmentize {
|
||||
my $raw = shift;
|
||||
|
||||
# In practice edifact uses latin-1 but check
|
||||
# Transport now converts to utf-8 on ingest
|
||||
# Do not convert here
|
||||
#my $char_set = 'iso-8859-1';
|
||||
#if ( $raw =~ m/^UNB[+]UNO(.)/ ) {
|
||||
# $char_set = msgcharset($1);
|
||||
#}
|
||||
#from_to( $raw, $char_set, 'utf8' );
|
||||
|
||||
my $re = qr{
|
||||
(?> # dont backtrack into this group
|
||||
[?]. # either the escape character
|
||||
# followed by any other character
|
||||
| # or
|
||||
[^'?] # a character that is neither escape
|
||||
# nor split
|
||||
)+
|
||||
}x;
|
||||
my @segmented;
|
||||
while ( $raw =~ /($re)/g ) {
|
||||
push @segmented, Koha::Edifact::Segment->new( { seg_string => $1 } );
|
||||
}
|
||||
return \@segmented;
|
||||
}
|
||||
|
||||
sub msgcharset {
|
||||
my $code = shift;
|
||||
if ( $code =~ m/^[^ABCDEF]$/ ) {
|
||||
$code = 'default';
|
||||
}
|
||||
my %encoding_map = (
|
||||
A => 'ascii',
|
||||
B => 'ascii',
|
||||
C => 'iso-8859-1',
|
||||
D => 'iso-8859-1',
|
||||
E => 'iso-8859-1',
|
||||
F => 'iso-8859-1',
|
||||
default => 'iso-8859-1',
|
||||
);
|
||||
return $encoding_map{$code};
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Edifact - Edifact message handler
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Koha module for parsing Edifact messages
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=head2 new
|
||||
|
||||
my $e = Koha::Edifact->new( { filename => 'myfilename' } );
|
||||
or
|
||||
my $e = Koha::Edifact->new( { transmission => $msg_variable } );
|
||||
|
||||
instantiate the Edifact parser, requires either to be passed an in-memory
|
||||
edifact message as transmission or a filename which it will read on creation
|
||||
|
||||
=head2 interchange_header
|
||||
|
||||
will return the data in the header field designated by the parameter
|
||||
specified. Valid parameters are: 'sender', 'recipient', 'datetime',
|
||||
'interchange_control_reference', and 'application_reference'
|
||||
|
||||
=head2 interchange_trailer
|
||||
|
||||
called either with the string 'interchange_control_count' or
|
||||
'interchange_control_reference' will return the corresponding field from
|
||||
the interchange trailer
|
||||
|
||||
=head2 new_data_iterator
|
||||
|
||||
Sets the object's data_iterator to point to the UNH segment
|
||||
|
||||
=head2 next_segment
|
||||
|
||||
Returns the next segment pointed to by the data_iterator. Increments the
|
||||
data_iterator member or destroys it if segment UNZ has been reached
|
||||
|
||||
=head2 get_transmission
|
||||
|
||||
This method is useful in debugg:ing. Call on an Edifact object
|
||||
it returns the object's transmission member
|
||||
|
||||
=head2 message_type
|
||||
|
||||
return the object's message type
|
||||
|
||||
=head2 message_array
|
||||
|
||||
return an array of Message objects contained in the Edifact transmission
|
||||
|
||||
=head1 Internal Methods
|
||||
|
||||
=head2 _init
|
||||
|
||||
Called by the constructor to do the parsing of the transmission
|
||||
|
||||
=head2 service_string_advice
|
||||
|
||||
Examines the Service String Advice returns 1 if the default separartors are in use
|
||||
undef otherwise
|
||||
|
||||
=head2 segmentize
|
||||
|
||||
takes a raw Edifact message and returns a reference to an array of
|
||||
its segments
|
||||
|
||||
=head2 msgcharset
|
||||
|
||||
Return the character set the message was encoded in. The default is iso-8859-1
|
||||
|
||||
We preserve this info but will have converted to utf-8 on ingest
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Colin Campbell <colin.campbell@ptfs-europe.com>
|
||||
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2014,2015, PTFS-Europe Ltd
|
||||
This program is free software, You may redistribute it under
|
||||
under the terms of the GNU General Public License
|
||||
|
||||
|
||||
=cut
|
||||
|
|
@ -0,0 +1,864 @@
|
|||
package Koha::Edifact::Line;
|
||||
|
||||
# Copyright 2014, 2015 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 3 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, see <http://www.gnu.org/licenses>.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use MARC::Record;
|
||||
use MARC::Field;
|
||||
use Carp;
|
||||
|
||||
sub new {
|
||||
my ( $class, $data_array_ref ) = @_;
|
||||
my $self = _parse_lines($data_array_ref);
|
||||
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
# helper routine used by constructor
|
||||
# creates the hashref used as a data structure by the Line object
|
||||
|
||||
sub _parse_lines {
|
||||
my $aref = shift;
|
||||
|
||||
my $lin = shift @{$aref};
|
||||
|
||||
my $id = $lin->elem( 2, 0 ); # may be undef in ordrsp
|
||||
my $action = $lin->elem( 1, 0 );
|
||||
my $d = {
|
||||
line_item_number => $lin->elem(0),
|
||||
action_notification => $action,
|
||||
item_number_id => $id,
|
||||
additional_product_ids => [],
|
||||
};
|
||||
my @item_description;
|
||||
|
||||
foreach my $s ( @{$aref} ) {
|
||||
if ( $s->tag eq 'PIA' ) {
|
||||
push @{ $d->{additional_product_ids} },
|
||||
{
|
||||
function_code => $s->elem(0),
|
||||
item_number => $s->elem( 1, 0 ),
|
||||
number_type => $s->elem( 1, 1 ),
|
||||
};
|
||||
}
|
||||
elsif ( $s->tag eq 'IMD' ) {
|
||||
push @item_description, $s;
|
||||
}
|
||||
elsif ( $s->tag eq 'QTY' ) {
|
||||
$d->{quantity} = $s->elem( 0, 1 );
|
||||
}
|
||||
elsif ( $s->tag eq 'DTM' ) {
|
||||
if ( $s->elem( 0, 0 ) eq '44' ) {
|
||||
$d->{availability_date} = $s->elem( 0, 1 );
|
||||
}
|
||||
}
|
||||
elsif ( $s->tag eq 'GIR' ) {
|
||||
|
||||
# we may get a Gir for each copy if QTY > 1
|
||||
if ( !$d->{GIR} ) {
|
||||
$d->{GIR} = [];
|
||||
push @{ $d->{GIR} }, extract_gir($s);
|
||||
}
|
||||
else {
|
||||
my $gir = extract_gir($s);
|
||||
if ( $gir->{copy} ) { # may have to merge
|
||||
foreach my $g ( @{ $d->{GIR} } ) {
|
||||
if ( $gir->{copy} eq $g->{copy} ) {
|
||||
foreach my $field ( keys %{$gir} ) {
|
||||
if ( !exists $g->{$field} ) {
|
||||
$g->{$field} = $gir->{$field};
|
||||
}
|
||||
}
|
||||
undef $gir;
|
||||
last;
|
||||
}
|
||||
}
|
||||
if ( defined $gir ) {
|
||||
push @{ $d->{GIR} }, $gir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ( $s->tag eq 'FTX' ) {
|
||||
|
||||
my $type = $s->elem(0);
|
||||
my $ctype = 'coded_free_text';
|
||||
if ( $type eq 'LNO' ) { # Ingrams Oasis Internal Notes field
|
||||
$type = 'internal_notes';
|
||||
$ctype = 'coded_internal_note';
|
||||
}
|
||||
elsif ( $type eq 'LIN' ) {
|
||||
$type = 'orderline_free_text';
|
||||
$ctype = 'coded_orderline_text';
|
||||
}
|
||||
elsif ( $type eq 'SUB' ) {
|
||||
$type = 'coded_substitute_text';
|
||||
}
|
||||
else {
|
||||
$type = 'free_text';
|
||||
}
|
||||
|
||||
my $coded_text = $s->elem(2);
|
||||
if ( ref $coded_text eq 'ARRAY' && $coded_text->[0] ) {
|
||||
$d->{$ctype}->{table} = $coded_text->[1];
|
||||
$d->{$ctype}->{code} = $coded_text->[0];
|
||||
}
|
||||
|
||||
my $ftx = $s->elem(3);
|
||||
if ( ref $ftx eq 'ARRAY' ) { # it comes in 70 character components
|
||||
$ftx = join q{ }, @{$ftx};
|
||||
}
|
||||
if ( exists $d->{$type} ) { # we can only catenate repeats
|
||||
$d->{$type} .= q{ };
|
||||
$d->{$type} .= $ftx;
|
||||
}
|
||||
else {
|
||||
$d->{$type} = $ftx;
|
||||
}
|
||||
}
|
||||
elsif ( $s->tag eq 'MOA' ) {
|
||||
|
||||
$d->{monetary_amount} = $s->elem( 0, 1 );
|
||||
}
|
||||
elsif ( $s->tag eq 'PRI' ) {
|
||||
|
||||
$d->{price} = $s->elem( 0, 1 );
|
||||
}
|
||||
elsif ( $s->tag eq 'RFF' ) {
|
||||
my $qualifier = $s->elem( 0, 0 );
|
||||
if ( $qualifier eq 'QLI' ) { # Suppliers unique quotation reference
|
||||
$d->{reference} = $s->elem( 0, 1 );
|
||||
}
|
||||
elsif ( $qualifier eq 'LI' ) { # Buyer's unique orderline number
|
||||
$d->{ordernumber} = $s->elem( 0, 1 );
|
||||
}
|
||||
elsif ( $qualifier eq 'SLI' )
|
||||
{ # Suppliers unique order line reference number
|
||||
$d->{orderline_reference_number} = $s->elem( 0, 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
$d->{item_description} = _format_item_description(@item_description);
|
||||
$d->{segs} = $aref;
|
||||
|
||||
return $d;
|
||||
}
|
||||
|
||||
sub _format_item_description {
|
||||
my @imd = @_;
|
||||
my $bibrec = {};
|
||||
|
||||
# IMD : +Type code 'L' + characteristic code 3 char + Description in comp 3 & 4
|
||||
foreach my $imd (@imd) {
|
||||
my $type_code = $imd->elem(0);
|
||||
my $ccode = $imd->elem(1);
|
||||
my $desc = $imd->elem( 2, 3 );
|
||||
if ( $imd->elem( 2, 4 ) ) {
|
||||
$desc .= $imd->elem( 2, 4 );
|
||||
}
|
||||
if ( $type_code ne 'L' ) {
|
||||
carp
|
||||
"Only handles text item descriptions at present: code=$type_code";
|
||||
next;
|
||||
}
|
||||
if ( exists $bibrec->{$ccode} ) {
|
||||
$bibrec->{$ccode} .= q{ };
|
||||
$bibrec->{$ccode} .= $desc;
|
||||
}
|
||||
else {
|
||||
$bibrec->{$ccode} = $desc;
|
||||
}
|
||||
}
|
||||
return $bibrec;
|
||||
}
|
||||
|
||||
sub marc_record {
|
||||
my $self = shift;
|
||||
my $b = $self->{item_description};
|
||||
|
||||
my $bib = MARC::Record->new();
|
||||
|
||||
my @spec;
|
||||
my @fields;
|
||||
if ( exists $b->{'010'} ) {
|
||||
@spec = qw( 100 a 011 c 012 b 013 d 014 e );
|
||||
push @fields, new_field( $b, [ 100, 1, q{ } ], @spec );
|
||||
}
|
||||
if ( exists $b->{'020'} ) {
|
||||
@spec = qw( 020 a 021 c 022 b 023 d 024 e );
|
||||
push @fields, new_field( $b, [ 700, 1, q{ } ], @spec );
|
||||
}
|
||||
|
||||
# corp conf
|
||||
if ( exists $b->{'030'} ) {
|
||||
push @fields, $self->corpcon(1);
|
||||
}
|
||||
if ( exists $b->{'040'} ) {
|
||||
push @fields, $self->corpcon(7);
|
||||
}
|
||||
if ( exists $b->{'050'} ) {
|
||||
@spec = qw( '050' a '060' b '065' c );
|
||||
push @fields, new_field( $b, [ 245, 1, 0 ], @spec );
|
||||
}
|
||||
if ( exists $b->{100} ) {
|
||||
@spec = qw( 100 a 101 b);
|
||||
push @fields, new_field( $b, [ 250, q{ }, q{ } ], @spec );
|
||||
}
|
||||
@spec = qw( 110 a 120 b 170 c );
|
||||
my $f = new_field( $b, [ 260, q{ }, q{ } ], @spec );
|
||||
if ($f) {
|
||||
push @fields, $f;
|
||||
}
|
||||
@spec = qw( 180 a 181 b 182 c 183 e);
|
||||
$f = new_field( $b, [ 300, q{ }, q{ } ], @spec );
|
||||
if ($f) {
|
||||
push @fields, $f;
|
||||
}
|
||||
if ( exists $b->{190} ) {
|
||||
@spec = qw( 190 a);
|
||||
push @fields, new_field( $b, [ 490, q{ }, q{ } ], @spec );
|
||||
}
|
||||
|
||||
if ( exists $b->{200} ) {
|
||||
@spec = qw( 200 a);
|
||||
push @fields, new_field( $b, [ 490, q{ }, q{ } ], @spec );
|
||||
}
|
||||
if ( exists $b->{210} ) {
|
||||
@spec = qw( 210 a);
|
||||
push @fields, new_field( $b, [ 490, q{ }, q{ } ], @spec );
|
||||
}
|
||||
if ( exists $b->{300} ) {
|
||||
@spec = qw( 300 a);
|
||||
push @fields, new_field( $b, [ 500, q{ }, q{ } ], @spec );
|
||||
}
|
||||
if ( exists $b->{310} ) {
|
||||
@spec = qw( 310 a);
|
||||
push @fields, new_field( $b, [ 520, q{ }, q{ } ], @spec );
|
||||
}
|
||||
if ( exists $b->{320} ) {
|
||||
@spec = qw( 320 a);
|
||||
push @fields, new_field( $b, [ 521, q{ }, q{ } ], @spec );
|
||||
}
|
||||
if ( exists $b->{260} ) {
|
||||
@spec = qw( 260 a);
|
||||
push @fields, new_field( $b, [ 600, q{ }, q{ } ], @spec );
|
||||
}
|
||||
if ( exists $b->{270} ) {
|
||||
@spec = qw( 270 a);
|
||||
push @fields, new_field( $b, [ 650, q{ }, q{ } ], @spec );
|
||||
}
|
||||
if ( exists $b->{280} ) {
|
||||
@spec = qw( 280 a);
|
||||
push @fields, new_field( $b, [ 655, q{ }, q{ } ], @spec );
|
||||
}
|
||||
|
||||
# class
|
||||
if ( exists $b->{230} ) {
|
||||
@spec = qw( 230 a);
|
||||
push @fields, new_field( $b, [ '082', q{ }, q{ } ], @spec );
|
||||
}
|
||||
if ( exists $b->{240} ) {
|
||||
@spec = qw( 240 a);
|
||||
push @fields, new_field( $b, [ '084', q{ }, q{ } ], @spec );
|
||||
}
|
||||
$bib->insert_fields_ordered(@fields);
|
||||
|
||||
return $bib;
|
||||
}
|
||||
|
||||
sub corpcon {
|
||||
my ( $self, $level ) = @_;
|
||||
my $test_these = {
|
||||
1 => [ '033', '032', '034' ],
|
||||
7 => [ '043', '042', '044' ],
|
||||
};
|
||||
my $conf = 0;
|
||||
foreach my $t ( @{ $test_these->{$level} } ) {
|
||||
if ( exists $self->{item_description}->{$t} ) {
|
||||
$conf = 1;
|
||||
}
|
||||
}
|
||||
my $tag;
|
||||
my @spec;
|
||||
my ( $i1, $i2 ) = ( q{ }, q{ } );
|
||||
if ($conf) {
|
||||
$tag = ( $level * 100 ) + 11;
|
||||
if ( $level == 1 ) {
|
||||
@spec = qw( 030 a 031 e 032 n 033 c 034 d);
|
||||
}
|
||||
else {
|
||||
@spec = qw( 040 a 041 e 042 n 043 c 044 d);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$tag = ( $level * 100 ) + 10;
|
||||
if ( $level == 1 ) {
|
||||
@spec = qw( 030 a 031 b);
|
||||
}
|
||||
else {
|
||||
@spec = qw( 040 a 041 b);
|
||||
}
|
||||
}
|
||||
return new_field( $self->{item_description}, [ $tag, $i1, $i2 ], @spec );
|
||||
}
|
||||
|
||||
sub new_field {
|
||||
my ( $b, $tag_ind, @sfd_elem ) = @_;
|
||||
my @sfd;
|
||||
while (@sfd_elem) {
|
||||
my $e = shift @sfd_elem;
|
||||
my $c = shift @sfd_elem;
|
||||
if ( exists $b->{$e} ) {
|
||||
push @sfd, $c, $b->{$e};
|
||||
}
|
||||
}
|
||||
if (@sfd) {
|
||||
my $field = MARC::Field->new( @{$tag_ind}, @sfd );
|
||||
return $field;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# Accessor methods to line data
|
||||
|
||||
sub item_number_id {
|
||||
my $self = shift;
|
||||
return $self->{item_number_id};
|
||||
}
|
||||
|
||||
sub line_item_number {
|
||||
my $self = shift;
|
||||
return $self->{line_item_number};
|
||||
}
|
||||
|
||||
sub additional_product_ids {
|
||||
my $self = shift;
|
||||
return $self->{additional_product_ids};
|
||||
}
|
||||
|
||||
sub action_notification {
|
||||
my $self = shift;
|
||||
my $a = $self->{action_notification};
|
||||
if ($a) {
|
||||
$a = _translate_action($a); # return the associated text string
|
||||
}
|
||||
return $a;
|
||||
}
|
||||
|
||||
sub item_description {
|
||||
my $self = shift;
|
||||
return $self->{item_description};
|
||||
}
|
||||
|
||||
sub monetary_amount {
|
||||
my $self = shift;
|
||||
return $self->{monetary_amount};
|
||||
}
|
||||
|
||||
sub quantity {
|
||||
my $self = shift;
|
||||
return $self->{quantity};
|
||||
}
|
||||
|
||||
sub price {
|
||||
my $self = shift;
|
||||
return $self->{price};
|
||||
}
|
||||
|
||||
sub reference {
|
||||
my $self = shift;
|
||||
return $self->{reference};
|
||||
}
|
||||
|
||||
sub orderline_reference_number {
|
||||
my $self = shift;
|
||||
return $self->{orderline_reference_number};
|
||||
}
|
||||
|
||||
sub ordernumber {
|
||||
my $self = shift;
|
||||
return $self->{ordernumber};
|
||||
}
|
||||
|
||||
sub free_text {
|
||||
my $self = shift;
|
||||
return $self->{free_text};
|
||||
}
|
||||
|
||||
sub coded_free_text {
|
||||
my $self = shift;
|
||||
return $self->{coded_free_text}->{code};
|
||||
}
|
||||
|
||||
sub internal_notes {
|
||||
my $self = shift;
|
||||
return $self->{internal_notes};
|
||||
}
|
||||
|
||||
sub coded_internal_note {
|
||||
my $self = shift;
|
||||
return $self->{coded_internal_note}->{code};
|
||||
}
|
||||
|
||||
sub orderline_free_text {
|
||||
my $self = shift;
|
||||
return $self->{orderline_free_text};
|
||||
}
|
||||
|
||||
sub coded_orderline_text {
|
||||
my $self = shift;
|
||||
my $code = $self->{coded_orderline_text}->{code};
|
||||
my $table = $self->{coded_orderline_text}->{table};
|
||||
my $txt;
|
||||
if ( $table eq '8B' || $table eq '7B' ) {
|
||||
$txt = translate_8B($code);
|
||||
}
|
||||
elsif ( $table eq '12B' ) {
|
||||
$txt = translate_12B($code);
|
||||
}
|
||||
if ( !$txt || $txt eq 'no match' ) {
|
||||
$txt = $code;
|
||||
}
|
||||
return $txt;
|
||||
}
|
||||
|
||||
sub substitute_free_text {
|
||||
my $self = shift;
|
||||
return $self->{substitute_free_text};
|
||||
}
|
||||
|
||||
sub coded_substitute_text {
|
||||
my $self = shift;
|
||||
return $self->{coded_substitute_text}->{code};
|
||||
}
|
||||
|
||||
# This will take a standard code as returned
|
||||
# by (orderline|substitue)-free_text (FTX seg LIN)
|
||||
# and expand it useing EditEUR code list 8B
|
||||
sub translate_8B {
|
||||
my ($code) = @_;
|
||||
|
||||
# list 7B is a subset of this
|
||||
my %code_list_8B = (
|
||||
AB => 'Publication abandoned',
|
||||
AD => 'Apply direct',
|
||||
AU => 'Publisher address unknown',
|
||||
CS => 'Status uncertain',
|
||||
FQ => 'Only available abroad',
|
||||
HK => 'Paperback OP: Hardback available',
|
||||
IB => 'In stock',
|
||||
IP => 'In print and in stock at publisher',
|
||||
MD => 'Manufactured on demand',
|
||||
NK => 'Item not known',
|
||||
NN => 'We do not supply this item',
|
||||
NP => 'Not yet published',
|
||||
NQ => 'Not stocked',
|
||||
NS => 'Not sold separately',
|
||||
OB => 'Temporarily out of stock',
|
||||
OF => 'This format out of print: other format available',
|
||||
OP => 'Out of print',
|
||||
OR => 'Out pf print; New Edition coming',
|
||||
PK => 'Hardback out of print: paperback available',
|
||||
PN => 'Publisher no longer in business',
|
||||
RE => 'Awaiting reissue',
|
||||
RF => 'refer to other publisher or distributor',
|
||||
RM => 'Remaindered',
|
||||
RP => 'Reprinting',
|
||||
RR => 'Rights restricted: cannot supply in this market',
|
||||
SD => 'Sold',
|
||||
SN => 'Our supplier cannot trace',
|
||||
SO => 'Pack or set not available: single items only',
|
||||
ST => 'Stocktaking: temporarily unavailable',
|
||||
TO => 'Only to order',
|
||||
TU => 'Temporarily unavailable',
|
||||
UB => 'Item unobtainable from our suppliers',
|
||||
UC => 'Unavailable@ reprint under consideration',
|
||||
);
|
||||
|
||||
if ( exists $code_list_8B{$code} ) {
|
||||
return $code_list_8B{$code};
|
||||
}
|
||||
else {
|
||||
return 'no match';
|
||||
}
|
||||
}
|
||||
|
||||
sub translate_12B {
|
||||
my ($code) = @_;
|
||||
|
||||
my %code_list_12B = (
|
||||
100 => 'Order line accepted',
|
||||
101 => 'Price query: orderline will be held awaiting customer response',
|
||||
102 =>
|
||||
'Discount query: order line will be held awaiting customer response',
|
||||
103 => 'Minimum order value not reached: order line will be held',
|
||||
104 =>
|
||||
'Firm order required: order line will be held awaiting customer response',
|
||||
110 => 'Order line accepted, substitute product will be supplied',
|
||||
200 => 'Order line not accepted',
|
||||
201 => 'Price query: order line not accepted',
|
||||
202 => 'Discount query: order line not accepted',
|
||||
203 => 'Minimum order value not reached: order line not accepted',
|
||||
205 => 'Order line not accepted: quoted promotion is invalid',
|
||||
206 => 'Order line not accepted: quoted promotion has ended',
|
||||
207 =>
|
||||
'Order line not accepted: customer ineligible for quoted promotion',
|
||||
210 => 'Order line not accepted: substitute product is offered',
|
||||
220 => 'Oustanding order line cancelled: reason unspecified',
|
||||
221 => 'Oustanding order line cancelled: past order expiry date',
|
||||
222 => 'Oustanding order line cancelled by customer request',
|
||||
223 => 'Oustanding order line cancelled: unable to supply',
|
||||
300 => 'Order line passed to new supplier',
|
||||
301 => 'Order line passed to secondhand department',
|
||||
400 => 'Backordered - awaiting supply',
|
||||
401 => 'On order from our supplier',
|
||||
402 => 'On order from abroad',
|
||||
403 => 'Backordered, waiting to reach minimum order value',
|
||||
404 => 'Despatched from our supplier, awaiting delivery',
|
||||
405 => 'Our supplier sent wrong item(s), re-ordered',
|
||||
406 => 'Our supplier sent short, re-ordered',
|
||||
407 => 'Our supplier sent damaged item(s), re-ordered',
|
||||
408 => 'Our supplier sent imperfect item(s), re-ordered',
|
||||
409 => 'Our supplier cannot trace order, re-ordered',
|
||||
410 => 'Ordered item(s) being processed by bookseller',
|
||||
411 =>
|
||||
'Ordered item(s) being processed by bookseller, awaiting customer action',
|
||||
412 => 'Order line held awaiting customer instruction',
|
||||
500 => 'Order line on hold - contact customer service',
|
||||
800 => 'Order line already despatched',
|
||||
900 => 'Cannot trace order line',
|
||||
901 => 'Order line held: note title change',
|
||||
902 => 'Order line held: note availability date delay',
|
||||
903 => 'Order line held: note price change',
|
||||
999 => 'Temporary hold: order action not yet determined',
|
||||
);
|
||||
|
||||
if ( exists $code_list_12B{$code} ) {
|
||||
return $code_list_12B{$code};
|
||||
}
|
||||
else {
|
||||
return 'no match';
|
||||
}
|
||||
}
|
||||
|
||||
# item_desription_fields accessors
|
||||
|
||||
sub title {
|
||||
my $self = shift;
|
||||
my $titlefield = q{050};
|
||||
if ( exists $self->{item_description}->{$titlefield} ) {
|
||||
return $self->{item_description}->{$titlefield};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub author {
|
||||
my $self = shift;
|
||||
my $field = q{010};
|
||||
if ( exists $self->{item_description}->{$field} ) {
|
||||
my $a = $self->{item_description}->{$field};
|
||||
my $forename_field = q{011};
|
||||
if ( exists $self->{item_description}->{$forename_field} ) {
|
||||
$a .= ', ';
|
||||
$a .= $self->{item_description}->{$forename_field};
|
||||
}
|
||||
return $a;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub series {
|
||||
my $self = shift;
|
||||
my $field = q{190};
|
||||
if ( exists $self->{item_description}->{$field} ) {
|
||||
return $self->{item_description}->{$field};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub publisher {
|
||||
my $self = shift;
|
||||
my $field = q{120};
|
||||
if ( exists $self->{item_description}->{$field} ) {
|
||||
return $self->{item_description}->{$field};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub publication_date {
|
||||
my $self = shift;
|
||||
my $field = q{170};
|
||||
if ( exists $self->{item_description}->{$field} ) {
|
||||
return $self->{item_description}->{$field};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub dewey_class {
|
||||
my $self = shift;
|
||||
my $field = q{230};
|
||||
if ( exists $self->{item_description}->{$field} ) {
|
||||
return $self->{item_description}->{$field};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub lc_class {
|
||||
my $self = shift;
|
||||
my $field = q{240};
|
||||
if ( exists $self->{item_description}->{$field} ) {
|
||||
return $self->{item_description}->{$field};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub girfield {
|
||||
my ( $self, $field, $occ ) = @_;
|
||||
if ( $self->number_of_girs ) {
|
||||
|
||||
# defaults to occurence 0 returns undef if occ requested > occs
|
||||
if ( defined $occ && $occ >= @{ $self->{GIR} } ) {
|
||||
return;
|
||||
}
|
||||
$occ ||= 0;
|
||||
return $self->{GIR}->[$occ]->{$field};
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sub number_of_girs {
|
||||
my $self = shift;
|
||||
if ( $self->{GIR} ) {
|
||||
|
||||
my $qty = @{ $self->{GIR} };
|
||||
|
||||
return $qty;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sub extract_gir {
|
||||
my $s = shift;
|
||||
my %qmap = (
|
||||
LAC => 'barcode',
|
||||
LAF => 'first_accession_number',
|
||||
LAL => 'last_accession_number',
|
||||
LCL => 'classification',
|
||||
LCO => 'item_unique_id',
|
||||
LCV => 'copy_value',
|
||||
LFH => 'feature_heading',
|
||||
LFN => 'fund_allocation',
|
||||
LFS => 'filing_suffix',
|
||||
LLN => 'loan_category',
|
||||
LLO => 'branch',
|
||||
LLS => 'label_sublocation',
|
||||
LQT => 'part_order_quantity',
|
||||
LRS => 'record_sublocation',
|
||||
LSM => 'shelfmark',
|
||||
LSQ => 'collection_code',
|
||||
LST => 'stock_category',
|
||||
LSZ => 'size_code',
|
||||
LVC => 'coded_servicing_instruction',
|
||||
LVT => 'servicing_instruction',
|
||||
);
|
||||
|
||||
my $set_qualifier = $s->elem( 0, 0 ); # copy number
|
||||
my $gir_element = { copy => $set_qualifier, };
|
||||
my $element = 1;
|
||||
while ( my $e = $s->elem($element) ) {
|
||||
++$element;
|
||||
if ( exists $qmap{ $e->[1] } ) {
|
||||
my $qualifier = $qmap{ $e->[1] };
|
||||
$gir_element->{$qualifier} = $e->[0];
|
||||
}
|
||||
else {
|
||||
|
||||
carp "Unrecognized GIR code : $e->[1] for $e->[0]";
|
||||
}
|
||||
}
|
||||
return $gir_element;
|
||||
}
|
||||
|
||||
# mainly for invoice processing amt_ will derive from MOA price_ from PRI and tax_ from TAX/MOA pairsn
|
||||
sub moa_amt {
|
||||
my ( $self, $qualifier ) = @_;
|
||||
foreach my $s ( @{ $self->{segs} } ) {
|
||||
if ( $s->tag eq 'MOA' && $s->elem( 0, 0 ) eq $qualifier ) {
|
||||
return $s->elem( 0, 1 );
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub amt_discount {
|
||||
my $self = shift;
|
||||
return $self->moa_amt('52');
|
||||
}
|
||||
|
||||
sub amt_prepayment {
|
||||
my $self = shift;
|
||||
return $self->moa_amt('113');
|
||||
}
|
||||
|
||||
# total including allowances & tax
|
||||
sub amt_total {
|
||||
my $self = shift;
|
||||
return $self->moa_amt('128');
|
||||
}
|
||||
|
||||
# Used to give price in currency other than that given in price
|
||||
sub amt_unitprice {
|
||||
my $self = shift;
|
||||
return $self->moa_amt('146');
|
||||
}
|
||||
|
||||
# item amount after allowances excluding tax
|
||||
sub amt_lineitem {
|
||||
my $self = shift;
|
||||
return $self->moa_amt('203');
|
||||
}
|
||||
|
||||
sub pri_price {
|
||||
my ( $self, $price_qualifier ) = @_;
|
||||
foreach my $s ( @{ $self->{segs} } ) {
|
||||
if ( $s->tag eq 'PRI' && $s->elem( 0, 0 ) eq $price_qualifier ) {
|
||||
return {
|
||||
price => $s->elem( 0, 1 ),
|
||||
type => $s->elem( 0, 2 ),
|
||||
type_qualifier => $s->elem( 0, 3 ),
|
||||
};
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# unit price that will be chaged excl tax
|
||||
sub price_net {
|
||||
my $self = shift;
|
||||
my $p = $self->pri_price('AAA');
|
||||
if ( defined $p ) {
|
||||
return $p->{price};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# unit price excluding all allowances, charges and taxes
|
||||
sub price_gross {
|
||||
my $self = shift;
|
||||
my $p = $self->pri_price('AAB');
|
||||
if ( defined $p ) {
|
||||
return $p->{price};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# information price incl tax excluding allowances, charges
|
||||
sub price_info {
|
||||
my $self = shift;
|
||||
my $p = $self->pri_price('AAE');
|
||||
if ( defined $p ) {
|
||||
return $p->{price};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# information price incl tax,allowances, charges
|
||||
sub price_info_inclusive {
|
||||
my $self = shift;
|
||||
my $p = $self->pri_price('AAE');
|
||||
if ( defined $p ) {
|
||||
return $p->{price};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub tax {
|
||||
my $self = shift;
|
||||
return $self->moa_amt('124');
|
||||
}
|
||||
|
||||
sub availability_date {
|
||||
my $self = shift;
|
||||
if ( exists $self->{availability_date} ) {
|
||||
return $self->{availability_date};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# return text string representing action code
|
||||
sub _translate_action {
|
||||
my $code = shift;
|
||||
my %action = (
|
||||
2 => 'cancelled',
|
||||
3 => 'change_requested',
|
||||
4 => 'no_action',
|
||||
5 => 'accepted',
|
||||
10 => 'not_found',
|
||||
24 => 'recorded', # Order accepted but a change notified
|
||||
);
|
||||
if ( $code && exists $action{$code} ) {
|
||||
return $action{$code};
|
||||
}
|
||||
return $code;
|
||||
|
||||
}
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::Edifact::Line
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Class to abstractly handle a Line in an Edifact Transmission
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Allows access to Edifact line elements by name
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
None documented at present
|
||||
|
||||
=head1 Methods
|
||||
|
||||
=head2 new
|
||||
|
||||
Called with an array ref of segments constituting the line
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Colin Campbell <colin.campbell@ptfs-europe.com>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2014,2015 PTFS-Europe Ltd
|
||||
This program is free software, You may redistribute it under
|
||||
under the terms of the GNU General Public License
|
||||
|
||||
|
||||
=cut
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
package Koha::Edifact::Message;
|
||||
|
||||
# Copyright 2014,2015 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 3 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, see <http://www.gnu.org/licenses>.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Koha::Edifact::Line;
|
||||
|
||||
sub new {
|
||||
my ( $class, $data_array_ref ) = @_;
|
||||
my $header = shift @{$data_array_ref};
|
||||
my $bgm = shift @{$data_array_ref};
|
||||
my $msg_function = $bgm->elem(2);
|
||||
my $dtm = [];
|
||||
while ( $data_array_ref->[0]->tag eq 'DTM' ) {
|
||||
push @{$dtm}, shift @{$data_array_ref};
|
||||
}
|
||||
|
||||
my $self = {
|
||||
function => $msg_function,
|
||||
header => $header,
|
||||
bgm => $bgm,
|
||||
message_reference_number => $header->elem(0),
|
||||
dtm => $dtm,
|
||||
datasegs => $data_array_ref,
|
||||
};
|
||||
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub message_refno {
|
||||
my $self = shift;
|
||||
return $self->{message_reference_number};
|
||||
}
|
||||
|
||||
sub function {
|
||||
my $self = shift;
|
||||
my $msg_function = $self->{bgm}->elem(2);
|
||||
if ( $msg_function == 9 ) {
|
||||
return 'original';
|
||||
}
|
||||
elsif ( $msg_function == 7 ) {
|
||||
return 'retransmission';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub message_reference_number {
|
||||
my $self = shift;
|
||||
return $self->{header}->elem(0);
|
||||
}
|
||||
|
||||
sub message_type {
|
||||
my $self = shift;
|
||||
return $self->{header}->elem( 1, 0 );
|
||||
}
|
||||
|
||||
sub message_code {
|
||||
my $self = shift;
|
||||
return $self->{bgm}->elem( 0, 0 );
|
||||
}
|
||||
|
||||
sub docmsg_number {
|
||||
my $self = shift;
|
||||
return $self->{bgm}->elem(1);
|
||||
}
|
||||
|
||||
sub message_date {
|
||||
my $self = shift;
|
||||
|
||||
# usually the first if not only dtm
|
||||
foreach my $d ( @{ $self->{dtm} } ) {
|
||||
if ( $d->elem( 0, 0 ) eq '137' ) {
|
||||
return $d->elem( 0, 1 );
|
||||
}
|
||||
}
|
||||
return; # this should not happen
|
||||
}
|
||||
|
||||
sub tax_point_date {
|
||||
my $self = shift;
|
||||
if ( $self->message_type eq 'INVOIC' ) {
|
||||
foreach my $d ( @{ $self->{dtm} } ) {
|
||||
if ( $d->elem( 0, 0 ) eq '131' ) {
|
||||
return $d->elem( 0, 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub expiry_date {
|
||||
my $self = shift;
|
||||
if ( $self->message_type eq 'QUOTES' ) {
|
||||
foreach my $d ( @{ $self->{dtm} } ) {
|
||||
if ( $d->elem( 0, 0 ) eq '36' ) {
|
||||
return $d->elem( 0, 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub shipment_charge {
|
||||
my $self = shift;
|
||||
|
||||
# A large number of different charges can be expressed at invoice and
|
||||
# item level but the only one koha takes cognizance of is shipment
|
||||
# should we wrap all invoice level charges into it??
|
||||
if ( $self->message_type eq 'INVOIC' ) {
|
||||
my $delivery = 0;
|
||||
my $amt = 0;
|
||||
foreach my $s ( @{ $self->{datasegs} } ) {
|
||||
if ( $s->tag eq 'LIN' ) {
|
||||
last;
|
||||
}
|
||||
if ( $s->tag eq 'ALC' ) {
|
||||
if ( $s->elem(0) eq 'C' ) { # Its a charge
|
||||
if ( $s->elem( 4, 0 ) eq 'DL' ) { # delivery charge
|
||||
$delivery = 1;
|
||||
}
|
||||
}
|
||||
next;
|
||||
}
|
||||
if ( $s->tag eq 'MOA' ) {
|
||||
$amt += $s->elem( 0, 1 );
|
||||
}
|
||||
}
|
||||
return $amt;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# return NAD fields
|
||||
|
||||
sub buyer_ean {
|
||||
my $self = shift;
|
||||
foreach my $s ( @{ $self->{datasegs} } ) {
|
||||
if ( $s->tag eq 'LIN' ) {
|
||||
last;
|
||||
}
|
||||
if ( $s->tag eq 'NAD' ) {
|
||||
my $qualifier = $s->elem(0);
|
||||
if ( $qualifier eq 'BY' ) {
|
||||
return $s->elem( 1, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub supplier_ean {
|
||||
my $self = shift;
|
||||
foreach my $s ( @{ $self->{datasegs} } ) {
|
||||
if ( $s->tag eq 'LIN' ) {
|
||||
last;
|
||||
}
|
||||
if ( $s->tag eq 'NAD' ) {
|
||||
my $qualifier = $s->elem(0);
|
||||
if ( $qualifier eq 'SU' ) {
|
||||
return $s->elem( 1, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
sub lineitems {
|
||||
my $self = shift;
|
||||
if ( $self->{quotation_lines} ) {
|
||||
return $self->{quotation_lines};
|
||||
}
|
||||
else {
|
||||
my $items = [];
|
||||
my $item_arr = [];
|
||||
foreach my $seg ( @{ $self->{datasegs} } ) {
|
||||
my $tag = $seg->tag;
|
||||
if ( $tag eq 'LIN' ) {
|
||||
if ( @{$item_arr} ) {
|
||||
push @{$items}, Koha::Edifact::Line->new($item_arr);
|
||||
}
|
||||
$item_arr = [$seg];
|
||||
next;
|
||||
}
|
||||
elsif ( $tag =~ m/^(UNS|CNT|UNT)$/sxm ) {
|
||||
if ( @{$item_arr} ) {
|
||||
push @{$items}, Koha::Edifact::Line->new($item_arr);
|
||||
}
|
||||
last;
|
||||
}
|
||||
else {
|
||||
if ( @{$item_arr} ) {
|
||||
push @{$item_arr}, $seg;
|
||||
}
|
||||
}
|
||||
}
|
||||
$self->{quotation_lines} = $items;
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::Edifact::Message
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Class modelling an Edifact Message for parsing
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 new
|
||||
|
||||
Passed an array of segments extracts message level info
|
||||
and parses lineitems as Line objects
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Colin Campbell <colin.campbell@ptfs-europe.com>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2014,2015 PTFS-Europe Ltd
|
||||
This program is free software, You may redistribute it under
|
||||
under the terms of the GNU General Public License
|
||||
|
||||
=cut
|
||||
|
|
@ -0,0 +1,831 @@
|
|||
package Koha::Edifact::Order;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
# Copyright 2014,2015 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 3 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, see <http://www.gnu.org/licenses>.
|
||||
|
||||
use Carp;
|
||||
use DateTime;
|
||||
use Readonly;
|
||||
use Business::ISBN;
|
||||
use Koha::Database;
|
||||
use C4::Budgets qw( GetBudget );
|
||||
|
||||
Readonly::Scalar my $seg_terminator => q{'};
|
||||
Readonly::Scalar my $separator => q{+};
|
||||
Readonly::Scalar my $component_separator => q{:};
|
||||
Readonly::Scalar my $release_character => q{?};
|
||||
|
||||
Readonly::Scalar my $NINES_12 => 999_999_999_999;
|
||||
Readonly::Scalar my $NINES_14 => 99_999_999_999_999;
|
||||
Readonly::Scalar my $CHUNKSIZE => 35;
|
||||
|
||||
sub new {
|
||||
my ( $class, $parameter_hashref ) = @_;
|
||||
|
||||
my $self = {};
|
||||
if ( ref $parameter_hashref ) {
|
||||
$self->{orderlines} = $parameter_hashref->{orderlines};
|
||||
$self->{recipient} = $parameter_hashref->{vendor};
|
||||
$self->{sender} = $parameter_hashref->{ean};
|
||||
$self->{is_response} = $parameter_hashref->{is_response};
|
||||
|
||||
# convenient alias
|
||||
$self->{basket} = $self->{orderlines}->[0]->basketno;
|
||||
$self->{message_date} = DateTime->now( time_zone => 'local' );
|
||||
}
|
||||
|
||||
# validate that its worth proceeding
|
||||
if ( !$self->{orderlines} ) {
|
||||
carp 'No orderlines passed to create order';
|
||||
return;
|
||||
}
|
||||
if ( !$self->{recipient} ) {
|
||||
carp
|
||||
"No vendor passed to order creation: basket = $self->{basket}->basketno()";
|
||||
return;
|
||||
}
|
||||
if ( !$self->{sender} ) {
|
||||
carp
|
||||
"No sender ean passed to order creation: basket = $self->{basket}->basketno()";
|
||||
return;
|
||||
}
|
||||
|
||||
# do this once per object not once per orderline
|
||||
my $database = Koha::Database->new();
|
||||
$self->{schema} = $database->schema;
|
||||
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub filename {
|
||||
my $self = shift;
|
||||
if ( !$self->{orderlines} ) {
|
||||
return;
|
||||
}
|
||||
my $filename = 'ordr' . $self->{basket}->basketno;
|
||||
$filename .= '.CEP';
|
||||
return $filename;
|
||||
}
|
||||
|
||||
sub encode {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->{interchange_control_reference} = int rand($NINES_14);
|
||||
$self->{message_count} = 0;
|
||||
|
||||
# $self->{segs}; # Message segments
|
||||
|
||||
$self->{transmission} = q{};
|
||||
|
||||
$self->{transmission} .= $self->initial_service_segments();
|
||||
|
||||
$self->{transmission} .= $self->user_data_message_segments();
|
||||
|
||||
$self->{transmission} .= $self->trailing_service_segments();
|
||||
return $self->{transmission};
|
||||
}
|
||||
|
||||
sub msg_date_string {
|
||||
my $self = shift;
|
||||
return $self->{message_date}->ymd();
|
||||
}
|
||||
|
||||
sub initial_service_segments {
|
||||
my $self = shift;
|
||||
|
||||
#UNA service string advice - specifies standard separators
|
||||
my $segs = _const('service_string_advice');
|
||||
|
||||
#UNB interchange header
|
||||
$segs .= $self->interchange_header();
|
||||
|
||||
#UNG functional group header NOT USED
|
||||
return $segs;
|
||||
}
|
||||
|
||||
sub interchange_header {
|
||||
my $self = shift;
|
||||
|
||||
# syntax identifier
|
||||
my $hdr =
|
||||
'UNB+UNOC:3'; # controling agency character set syntax version number
|
||||
# Interchange Sender
|
||||
$hdr .= _interchange_sr_identifier( $self->{sender}->ean,
|
||||
$self->{sender}->id_code_qualifier ); # interchange sender
|
||||
$hdr .= _interchange_sr_identifier( $self->{recipient}->san,
|
||||
$self->{recipient}->id_code_qualifier ); # interchange Recipient
|
||||
|
||||
$hdr .= $separator;
|
||||
|
||||
# DateTime of preparation
|
||||
$hdr .= $self->{message_date}->format_cldr('yyMMdd:HHmm');
|
||||
$hdr .= $separator;
|
||||
$hdr .= $self->interchange_control_reference();
|
||||
$hdr .= $separator;
|
||||
|
||||
# Recipents reference password not usually used in edifact
|
||||
$hdr .= q{+ORDERS}; # application reference
|
||||
|
||||
#Edifact does not usually include the following
|
||||
# $hdr .= $separator; # Processing priority not usually used in edifact
|
||||
# $hdr .= $separator; # Acknowledgewment request : not usually used in edifact
|
||||
# $hdr .= q{+EANCOM} # Communications agreement id
|
||||
# $hdr .= q{+1} # Test indicator
|
||||
#
|
||||
$hdr .= $seg_terminator;
|
||||
return $hdr;
|
||||
}
|
||||
|
||||
sub user_data_message_segments {
|
||||
my $self = shift;
|
||||
|
||||
#UNH message_header :: seg count begins here
|
||||
$self->message_header();
|
||||
|
||||
$self->order_msg_header();
|
||||
|
||||
my $line_number = 0;
|
||||
foreach my $ol ( @{ $self->{orderlines} } ) {
|
||||
++$line_number;
|
||||
$self->order_line( $line_number, $ol );
|
||||
}
|
||||
|
||||
$self->message_trailer();
|
||||
|
||||
my $data_segment_string = join q{}, @{ $self->{segs} };
|
||||
return $data_segment_string;
|
||||
}
|
||||
|
||||
sub message_trailer {
|
||||
my $self = shift;
|
||||
|
||||
# terminate the message
|
||||
$self->add_seg("UNS+S$seg_terminator");
|
||||
|
||||
# CNT Control_Total
|
||||
# Could be (code 1) total value of QTY segments
|
||||
# or ( code = 2 ) number of lineitems
|
||||
my $num_orderlines = @{ $self->{orderlines} };
|
||||
$self->add_seg("CNT+2:$num_orderlines$seg_terminator");
|
||||
|
||||
# UNT Message Trailer
|
||||
my $segments_in_message =
|
||||
1 + @{ $self->{segs} }; # count incl UNH & UNT (!!this one)
|
||||
my $reference = $self->message_reference('current');
|
||||
$self->add_seg("UNT+$segments_in_message+$reference$seg_terminator");
|
||||
return;
|
||||
}
|
||||
|
||||
sub trailing_service_segments {
|
||||
my $self = shift;
|
||||
my $trailer = q{};
|
||||
|
||||
#UNE functional group trailer NOT USED
|
||||
#UNZ interchange trailer
|
||||
$trailer .= $self->interchange_trailer();
|
||||
|
||||
return $trailer;
|
||||
}
|
||||
|
||||
sub interchange_control_reference {
|
||||
my $self = shift;
|
||||
if ( $self->{interchange_control_reference} ) {
|
||||
return sprintf '%014d', $self->{interchange_control_reference};
|
||||
}
|
||||
else {
|
||||
carp 'calling for ref of unencoded order';
|
||||
return 'NONE ASSIGNED';
|
||||
}
|
||||
}
|
||||
|
||||
sub message_reference {
|
||||
my ( $self, $function ) = @_;
|
||||
if ( $function eq 'new' || !$self->{message_reference_no} ) {
|
||||
|
||||
# unique 14 char mesage ref
|
||||
$self->{message_reference_no} = sprintf 'ME%012d', int rand($NINES_12);
|
||||
}
|
||||
return $self->{message_reference_no};
|
||||
}
|
||||
|
||||
sub message_header {
|
||||
my $self = shift;
|
||||
|
||||
$self->{segs} = []; # initialize the message
|
||||
$self->{message_count}++; # In practice alwaya 1
|
||||
|
||||
my $hdr = q{UNH+} . $self->message_reference('new');
|
||||
$hdr .= _const('message_identifier');
|
||||
$self->add_seg($hdr);
|
||||
return;
|
||||
}
|
||||
|
||||
sub interchange_trailer {
|
||||
my $self = shift;
|
||||
|
||||
my $t = "UNZ+$self->{message_count}+";
|
||||
$t .= $self->interchange_control_reference;
|
||||
$t .= $seg_terminator;
|
||||
return $t;
|
||||
}
|
||||
|
||||
sub order_msg_header {
|
||||
my $self = shift;
|
||||
my @header;
|
||||
|
||||
# UNH see message_header
|
||||
# BGM
|
||||
push @header,
|
||||
beginning_of_message(
|
||||
$self->{basket}->basketno,
|
||||
$self->{recipient}->san,
|
||||
$self->{is_response}
|
||||
);
|
||||
|
||||
# DTM
|
||||
push @header, message_date_segment( $self->{message_date} );
|
||||
|
||||
# NAD-RFF buyer supplier ids
|
||||
push @header,
|
||||
name_and_address(
|
||||
'BUYER',
|
||||
$self->{sender}->ean,
|
||||
$self->{sender}->id_code_qualifier
|
||||
);
|
||||
push @header,
|
||||
name_and_address(
|
||||
'SUPPLIER',
|
||||
$self->{recipient}->san,
|
||||
$self->{recipient}->id_code_qualifier
|
||||
);
|
||||
|
||||
# repeat for for other relevant parties
|
||||
|
||||
# CUX currency
|
||||
# ISO 4217 code to show default currency prices are quoted in
|
||||
# e.g. CUX+2:GBP:9'
|
||||
# TBD currency handling
|
||||
|
||||
$self->add_seg(@header);
|
||||
return;
|
||||
}
|
||||
|
||||
sub beginning_of_message {
|
||||
my $basketno = shift;
|
||||
my $supplier_san = shift;
|
||||
my $response = shift;
|
||||
my $document_message_no = sprintf '%011d', $basketno;
|
||||
|
||||
# Peters & Bolinda use the BIC recommendation to use 22V a code not in Edifact
|
||||
# If the order is in response to a quote
|
||||
my %bic_sans = (
|
||||
'5013546025065' => 'Peters',
|
||||
'9377779308820' => 'Bolinda',
|
||||
);
|
||||
|
||||
# my $message_function = 9; # original 7 = retransmission
|
||||
# message_code values
|
||||
# 220 prder
|
||||
# 224 rush order
|
||||
# 228 sample order :: order for approval / inspection copies
|
||||
# 22C continuation order for volumes in a set etc.
|
||||
# my $message_code = '220';
|
||||
if ( exists $bic_sans{$supplier_san} && $response ) {
|
||||
return "BGM+22V+$document_message_no+9$seg_terminator";
|
||||
}
|
||||
|
||||
return "BGM+220+$document_message_no+9$seg_terminator";
|
||||
}
|
||||
|
||||
sub name_and_address {
|
||||
my ( $party, $id_code, $id_agency ) = @_;
|
||||
my %qualifier_code = (
|
||||
BUYER => 'BY',
|
||||
DELIVERY => 'DP', # delivery location if != buyer
|
||||
INVOICEE => 'IV', # if different from buyer
|
||||
SUPPLIER => 'SU',
|
||||
);
|
||||
if ( !exists $qualifier_code{$party} ) {
|
||||
carp "No qualifier code for $party";
|
||||
return;
|
||||
}
|
||||
if ( $id_agency eq '14' ) {
|
||||
$id_agency = '9'; # ean coded differently in this seg
|
||||
}
|
||||
|
||||
return "NAD+$qualifier_code{$party}+${id_code}::$id_agency$seg_terminator";
|
||||
}
|
||||
|
||||
sub order_line {
|
||||
my ( $self, $linenumber, $orderline ) = @_;
|
||||
|
||||
my $schema = $self->{schema};
|
||||
if ( !$orderline->biblionumber )
|
||||
{ # cannot generate an orderline without a bib record
|
||||
return;
|
||||
}
|
||||
my $biblionumber = $orderline->biblionumber->biblionumber;
|
||||
my @biblioitems = $schema->resultset('Biblioitem')
|
||||
->search( { biblionumber => $biblionumber, } );
|
||||
my $biblioitem = $biblioitems[0]; # makes the assumption there is 1 only
|
||||
# or else all have same details
|
||||
|
||||
my $id_string = $orderline->line_item_id;
|
||||
|
||||
# LIN line-number in msg :: if we had a 13 digit ean we could add
|
||||
$self->add_seg( lin_segment( $linenumber, $id_string ) );
|
||||
|
||||
# PIA isbn or other id
|
||||
my @identifiers;
|
||||
foreach my $id ( $biblioitem->ean, $biblioitem->issn, $biblioitem->isbn ) {
|
||||
if ( $id && $id ne $id_string ) {
|
||||
push @identifiers, $id;
|
||||
}
|
||||
}
|
||||
$self->add_seg( additional_product_id( join( ' ', @identifiers ) ) );
|
||||
|
||||
# biblio description
|
||||
$self->add_seg( item_description( $orderline->biblionumber, $biblioitem ) );
|
||||
|
||||
# QTY order quantity
|
||||
my $qty = join q{}, 'QTY+21:', $orderline->quantity, $seg_terminator;
|
||||
$self->add_seg($qty);
|
||||
|
||||
# DTM Optional date constraints on delivery
|
||||
# we dont currently support this in koha
|
||||
# GIR copy-related data
|
||||
my @items;
|
||||
if ( C4::Context->preference('AcqCreateItem') eq 'ordering' ) {
|
||||
my @linked_itemnumbers = $orderline->aqorders_items;
|
||||
|
||||
foreach my $item (@linked_itemnumbers) {
|
||||
my $i_obj = $schema->resultset('Item')->find( $item->itemnumber );
|
||||
if ( defined $i_obj ) {
|
||||
push @items, $i_obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
my $item_hash = {
|
||||
itemtype => $biblioitem->itemtype,
|
||||
shelfmark => $biblioitem->cn_class,
|
||||
};
|
||||
my $branch = $orderline->basketno->deliveryplace;
|
||||
if ($branch) {
|
||||
$item_hash->{branch} = $branch;
|
||||
}
|
||||
for ( 1 .. $orderline->quantity ) {
|
||||
push @items, $item_hash;
|
||||
}
|
||||
}
|
||||
my $budget = GetBudget( $orderline->budget_id );
|
||||
my $ol_fields = { budget_code => $budget->{budget_code}, };
|
||||
if ( $orderline->order_vendornote ) {
|
||||
$ol_fields->{servicing_instruction} = $orderline->order_vendornote;
|
||||
}
|
||||
$self->add_seg( gir_segments( $ol_fields, @items ) );
|
||||
|
||||
# TBD what if #items exceeds quantity
|
||||
|
||||
# FTX free text for current orderline TBD
|
||||
# dont really have a special instructions field to encode here
|
||||
# Encode notes here
|
||||
# PRI-CUX-DTM unit price on which order is placed : optional
|
||||
# Coutts read this as 0.00 if not present
|
||||
if ( $orderline->listprice ) {
|
||||
my $price = sprintf 'PRI+AAE:%.2f:CA', $orderline->listprice;
|
||||
$price .= $seg_terminator;
|
||||
$self->add_seg($price);
|
||||
}
|
||||
|
||||
# RFF unique orderline reference no
|
||||
my $rff = join q{}, 'RFF+LI:', $orderline->ordernumber, $seg_terminator;
|
||||
$self->add_seg($rff);
|
||||
|
||||
# RFF : suppliers unique quotation reference number
|
||||
if ( $orderline->suppliers_reference_number ) {
|
||||
$rff = join q{}, 'RFF+', $orderline->suppliers_reference_qualifier,
|
||||
':', $orderline->suppliers_reference_number, $seg_terminator;
|
||||
$self->add_seg($rff);
|
||||
}
|
||||
|
||||
# LOC-QTY multiple delivery locations
|
||||
#TBD to specify extra delivery locs
|
||||
# NAD order line name and address
|
||||
#TBD Optionally indicate a name & address or order originator
|
||||
# TDT method of delivey ol-specific
|
||||
# TBD requests a special delivery option
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub item_description {
|
||||
my ( $bib, $biblioitem ) = @_;
|
||||
my $bib_desc = {
|
||||
author => $bib->author,
|
||||
title => $bib->title,
|
||||
publisher => $biblioitem->publishercode,
|
||||
year => $biblioitem->publicationyear,
|
||||
};
|
||||
|
||||
my @itm = ();
|
||||
|
||||
# 009 Author
|
||||
# 050 Title :: title
|
||||
# 080 Vol/Part no
|
||||
# 100 Edition statement
|
||||
# 109 Publisher :: publisher
|
||||
# 110 place of pub
|
||||
# 170 Date of publication :: year
|
||||
# 220 Binding :: binding
|
||||
my %code = (
|
||||
author => '009',
|
||||
title => '050',
|
||||
publisher => '109',
|
||||
year => '170',
|
||||
binding => '220',
|
||||
);
|
||||
for my $field (qw(author title publisher year binding )) {
|
||||
if ( $bib_desc->{$field} ) {
|
||||
my $data = encode_text( $bib_desc->{$field} );
|
||||
push @itm, imd_segment( $code{$field}, $data );
|
||||
}
|
||||
}
|
||||
|
||||
return @itm;
|
||||
}
|
||||
|
||||
sub imd_segment {
|
||||
my ( $code, $data ) = @_;
|
||||
|
||||
my $seg_prefix = "IMD+L+$code+:::";
|
||||
|
||||
# chunk_line
|
||||
my @chunks;
|
||||
while ( my $x = substr $data, 0, $CHUNKSIZE, q{} ) {
|
||||
if ( length $x == $CHUNKSIZE ) {
|
||||
if ( $x =~ s/([?]{1,2})$// ) {
|
||||
$data = "$1$data"; # dont breakup ?' ?? etc
|
||||
}
|
||||
}
|
||||
push @chunks, $x;
|
||||
}
|
||||
my @segs;
|
||||
my $odd = 1;
|
||||
foreach my $c (@chunks) {
|
||||
if ($odd) {
|
||||
push @segs, "$seg_prefix$c";
|
||||
}
|
||||
else {
|
||||
$segs[-1] .= ":$c$seg_terminator";
|
||||
}
|
||||
$odd = !$odd;
|
||||
}
|
||||
if ( @segs && $segs[-1] !~ m/$seg_terminator$/o ) {
|
||||
$segs[-1] .= $seg_terminator;
|
||||
}
|
||||
return @segs;
|
||||
}
|
||||
|
||||
sub gir_segments {
|
||||
my ( $orderfields, @onorderitems ) = @_;
|
||||
|
||||
my $budget_code = $orderfields->{budget_code};
|
||||
my @segments;
|
||||
my $sequence_no = 1;
|
||||
foreach my $item (@onorderitems) {
|
||||
my $seg = sprintf 'GIR+%03d', $sequence_no;
|
||||
$seg .= add_gir_identity_number( 'LFN', $budget_code );
|
||||
if ( C4::Context->preference('AcqCreateItem') eq 'ordering' ) {
|
||||
$seg .=
|
||||
add_gir_identity_number( 'LLO', $item->homebranch->branchcode );
|
||||
$seg .= add_gir_identity_number( 'LST', $item->itype );
|
||||
$seg .= add_gir_identity_number( 'LSQ', $item->location );
|
||||
$seg .= add_gir_identity_number( 'LSM', $item->itemcallnumber );
|
||||
|
||||
# itemcallnumber -> shelfmark
|
||||
}
|
||||
else {
|
||||
if ( $item->{branch} ) {
|
||||
$seg .= add_gir_identity_number( 'LLO', $item->{branch} );
|
||||
}
|
||||
$seg .= add_gir_identity_number( 'LST', $item->{itemtype} );
|
||||
$seg .= add_gir_identity_number( 'LSM', $item->{shelfmark} );
|
||||
}
|
||||
if ( $orderfields->{servicing_instruction} ) {
|
||||
$seg .= add_gir_identity_number( 'LVT',
|
||||
$orderfields->{servicing_instruction} );
|
||||
}
|
||||
$sequence_no++;
|
||||
push @segments, $seg;
|
||||
}
|
||||
return @segments;
|
||||
}
|
||||
|
||||
sub add_gir_identity_number {
|
||||
my ( $number_qualifier, $number ) = @_;
|
||||
if ($number) {
|
||||
return "+${number}:${number_qualifier}";
|
||||
}
|
||||
return q{};
|
||||
}
|
||||
|
||||
sub add_seg {
|
||||
my ( $self, @s ) = @_;
|
||||
foreach my $segment (@s) {
|
||||
if ( $segment !~ m/$seg_terminator$/o ) {
|
||||
$segment .= $seg_terminator;
|
||||
}
|
||||
}
|
||||
push @{ $self->{segs} }, @s;
|
||||
return;
|
||||
}
|
||||
|
||||
sub lin_segment {
|
||||
my ( $line_number, $item_number_id ) = @_;
|
||||
|
||||
if ($item_number_id) {
|
||||
$item_number_id = "++${item_number_id}:EN";
|
||||
}
|
||||
else {
|
||||
$item_number_id = q||;
|
||||
}
|
||||
|
||||
return "LIN+$line_number$item_number_id$seg_terminator";
|
||||
}
|
||||
|
||||
sub additional_product_id {
|
||||
my $isbn_field = shift;
|
||||
my ( $product_id, $product_code );
|
||||
if ( $isbn_field =~ m/(\d{13})/ ) {
|
||||
$product_id = $1;
|
||||
$product_code = 'EN';
|
||||
}
|
||||
elsif ( $isbn_field =~ m/(\d{9})[Xx\d]/ ) {
|
||||
$product_id = $1;
|
||||
$product_code = 'IB';
|
||||
}
|
||||
|
||||
# TBD we could have a manufacturers no issn etc
|
||||
if ( !$product_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
# function id set to 5 states this is the main product id
|
||||
return "PIA+5+$product_id:$product_code$seg_terminator";
|
||||
}
|
||||
|
||||
sub message_date_segment {
|
||||
my $dt = shift;
|
||||
|
||||
# qualifier:message_date:format_code
|
||||
|
||||
my $message_date = $dt->ymd(q{}); # no sep in edifact format
|
||||
|
||||
return "DTM+137:$message_date:102$seg_terminator";
|
||||
}
|
||||
|
||||
sub _const {
|
||||
my $key = shift;
|
||||
Readonly my %S => {
|
||||
service_string_advice => q{UNA:+.? '},
|
||||
message_identifier => q{+ORDERS:D:96A:UN:EAN008'},
|
||||
};
|
||||
return ( $S{$key} ) ? $S{$key} : q{};
|
||||
}
|
||||
|
||||
sub _interchange_sr_identifier {
|
||||
my ( $identification, $qualifier ) = @_;
|
||||
|
||||
if ( !$identification ) {
|
||||
$identification = 'RANDOM';
|
||||
$qualifier = '92';
|
||||
carp 'undefined identifier';
|
||||
}
|
||||
|
||||
# 14 EAN International
|
||||
# 31B US SAN (preferred)
|
||||
# also 91 assigned by supplier
|
||||
# also 92 assigned by buyer
|
||||
if ( $qualifier !~ m/^(?:14|31B|91|92)/xms ) {
|
||||
$qualifier = '92';
|
||||
}
|
||||
|
||||
return "+$identification:$qualifier";
|
||||
}
|
||||
|
||||
sub encode_text {
|
||||
my $string = shift;
|
||||
if ($string) {
|
||||
$string =~ s/[?]/??/g;
|
||||
$string =~ s/'/?'/g;
|
||||
$string =~ s/:/?:/g;
|
||||
$string =~ s/[+]/?+/g;
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::Edifact::Order
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Format an Edifact Order message from a Koha basket
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Generates an Edifact format Order message for a Koha basket.
|
||||
Normally the only methods used directly by the caller would be
|
||||
new to set up the message, encode to return the formatted message
|
||||
and filename to obtain a name under which to store the message
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Should integrate into Koha::Edifact namespace
|
||||
Can caller interface be made cleaner?
|
||||
Make handling of GIR segments more customizable
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 new
|
||||
|
||||
my $edi_order = Edifact::Order->new(
|
||||
orderlines => \@orderlines,
|
||||
vendor => $vendor_edi_account,
|
||||
ean => $library_ean
|
||||
);
|
||||
|
||||
instantiate the Edifact::Order object, all parameters are Schema::Resultset objects
|
||||
Called in Koha::Edifact create_edi_order
|
||||
|
||||
=head2 filename
|
||||
|
||||
my $filename = $edi_order->filename()
|
||||
|
||||
returns a filename for the edi order. The filename embeds a reference to the
|
||||
basket the message was created to encode
|
||||
|
||||
=head2 encode
|
||||
|
||||
my $edifact_message = $edi_order->encode();
|
||||
|
||||
Encodes the basket as a valid edifact message ready for transmission
|
||||
|
||||
=head2 initial_service_segments
|
||||
|
||||
Creates the service segments which begin the message
|
||||
|
||||
=head2 interchange_header
|
||||
|
||||
Return an interchange header encoding sender and recipient
|
||||
ids message date and standards
|
||||
|
||||
=head2 user_data_message_segments
|
||||
|
||||
Include message data within the encoded message
|
||||
|
||||
=head2 message_trailer
|
||||
|
||||
Terminate message data including control data on number
|
||||
of messages and segments included
|
||||
|
||||
=head2 trailing_service_segments
|
||||
|
||||
Include the service segments occuring at the end of the message
|
||||
=head2 interchange_control_reference
|
||||
|
||||
Returns the unique interchange control reference as a 14 digit number
|
||||
|
||||
=head2 message_reference
|
||||
|
||||
On generates and subsequently returns the unique message
|
||||
reference number as a 12 digit number preceded by ME, to generate a new number
|
||||
pass the string 'new'.
|
||||
In practice we encode 1 message per transmission so there is only one message
|
||||
referenced. were we to encode multiple messages a new reference would be
|
||||
neaded for each
|
||||
|
||||
=head2 message_header
|
||||
|
||||
Commences a new message
|
||||
|
||||
=head2 interchange_trailer
|
||||
|
||||
returns the UNZ segment which ends the tranmission encoding the
|
||||
message count and control reference for the interchange
|
||||
|
||||
=head2 order_msg_header
|
||||
|
||||
Formats the message header segments
|
||||
|
||||
=head2 beginning_of_message
|
||||
|
||||
Returns the BGM segment which includes the Koha basket number
|
||||
|
||||
=head2 name_and_address
|
||||
|
||||
Parameters: Function ( BUYER, DELIVERY, INVOICE, SUPPLIER)
|
||||
Id
|
||||
Agency
|
||||
|
||||
Returns a NAD segment containg the id and agency for for the Function
|
||||
value. Handles the fact that NAD segments encode the value for 'EAN' differently
|
||||
to elsewhere.
|
||||
|
||||
=head2 order_line
|
||||
|
||||
Creates the message segments wncoding an order line
|
||||
|
||||
=head2 item_description
|
||||
|
||||
Encodes the biblio item fields Author, title, publisher, date of publication
|
||||
binding
|
||||
|
||||
=head2 imd_segment
|
||||
|
||||
Formats an IMD segment, handles the chunking of data into the 35 character
|
||||
lengths required and the creation of repeat segments
|
||||
|
||||
=head2 gir_segments
|
||||
|
||||
Add item level information
|
||||
|
||||
=head2 add_gir_identity_number
|
||||
|
||||
Handle the formatting of a GIR element
|
||||
return empty string if no data
|
||||
|
||||
=head2 add_seg
|
||||
|
||||
Adds a parssed array of segments to the objects segment list
|
||||
ensures all segments are properly terminated by '
|
||||
|
||||
=head2 lin_segment
|
||||
|
||||
Adds a LIN segment consisting of the line number and the ean number
|
||||
if the passed isbn is valid
|
||||
|
||||
=head2 additional_product_id
|
||||
|
||||
Add a PIA segment for an additional product id
|
||||
|
||||
=head2 message_date_segment
|
||||
|
||||
Passed a DateTime object returns a correctly formatted DTM segment
|
||||
|
||||
=head2 _const
|
||||
|
||||
Stores and returns constant strings for service_string_advice
|
||||
and message_identifier
|
||||
TBD replace with class variables
|
||||
|
||||
=head2 _interchange_sr_identifier
|
||||
|
||||
Format sender and receipient identifiers for use in the interchange header
|
||||
|
||||
=head2 encode_text
|
||||
|
||||
Encode textual data into the standard character set ( iso 8859-1 )
|
||||
and quote any Edifact metacharacters
|
||||
|
||||
=head2 msg_date_string
|
||||
|
||||
Convenient routine which returns message date as a Y-m-d string
|
||||
useful if the caller wants to log date of creation
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Colin Campbell <colin.campbell@ptfs-europe.com>
|
||||
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2014,2015,2016 PTFS-Europe Ltd
|
||||
This program is free software, You may redistribute it under
|
||||
under the terms of the GNU General Public License
|
||||
|
||||
|
||||
=cut
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
package Koha::Edifact::Segment;
|
||||
|
||||
# Copyright 2014,2016 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 3 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, see <http://www.gnu.org/licenses>.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
sub new {
|
||||
my ( $class, $parm_ref ) = @_;
|
||||
my $self = {};
|
||||
if ( $parm_ref->{seg_string} ) {
|
||||
$self = _parse_seg( $parm_ref->{seg_string} );
|
||||
}
|
||||
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub tag {
|
||||
my $self = shift;
|
||||
return $self->{tag};
|
||||
}
|
||||
|
||||
# return specified element may be data or an array ref if components
|
||||
sub elem {
|
||||
my ( $self, $element_number, $component_number ) = @_;
|
||||
if ( $element_number < @{ $self->{elem_arr} } ) {
|
||||
|
||||
my $e = $self->{elem_arr}->[$element_number];
|
||||
if ( defined $component_number ) {
|
||||
if ( ref $e eq 'ARRAY' ) {
|
||||
if ( $component_number < @{$e} ) {
|
||||
return $e->[$component_number];
|
||||
}
|
||||
}
|
||||
elsif ( $component_number == 0 ) {
|
||||
|
||||
# a string could be an element with a single component
|
||||
return $e;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
return; #element undefined ( out of range
|
||||
}
|
||||
|
||||
sub element {
|
||||
my ( $self, @params ) = @_;
|
||||
|
||||
return $self->elem(@params);
|
||||
}
|
||||
|
||||
sub as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $string = $self->{tag};
|
||||
foreach my $e ( @{ $self->{elem_arr} } ) {
|
||||
$string .= q|+|;
|
||||
if ( ref $e eq 'ARRAY' ) {
|
||||
$string .= join q{:}, @{$e};
|
||||
}
|
||||
else {
|
||||
$string .= $e;
|
||||
}
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
# parse a string into fields
|
||||
sub _parse_seg {
|
||||
my $s = shift;
|
||||
my $e = {
|
||||
tag => substr( $s, 0, 3 ),
|
||||
elem_arr => _get_elements( substr( $s, 3 ) ),
|
||||
};
|
||||
return $e;
|
||||
}
|
||||
|
||||
##
|
||||
# String parsing
|
||||
#
|
||||
|
||||
sub _get_elements {
|
||||
my $seg = shift;
|
||||
|
||||
$seg =~ s/^[+]//; # dont start with a dummy element`
|
||||
my @elem_array = map { _components($_) } split /(?<![?])[+]/, $seg;
|
||||
|
||||
return \@elem_array;
|
||||
}
|
||||
|
||||
sub _components {
|
||||
my $element = shift;
|
||||
my @c = split /(?<![?])[:]/, $element;
|
||||
if ( @c == 1 ) { # single element return a string
|
||||
return de_escape( $c[0] );
|
||||
}
|
||||
@c = map { de_escape($_) } @c;
|
||||
return \@c;
|
||||
}
|
||||
|
||||
sub de_escape {
|
||||
my $string = shift;
|
||||
|
||||
# remove escaped characters from the component string
|
||||
$string =~ s/[?]([:?+'])/$1/g;
|
||||
return $string;
|
||||
}
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::Edifact::Segment - Class foe Edifact Segments
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Used by Koha::Edifact to represent segments in a parsed Edifact message
|
||||
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 new
|
||||
|
||||
my $s = Koha::Edifact::Segment->new( { seg_string => $raw });
|
||||
|
||||
passed a string representation of the segment, parses it
|
||||
and retums a Segment object
|
||||
|
||||
=head2 tag
|
||||
|
||||
returns the three character segment tag
|
||||
|
||||
=head2 elem
|
||||
|
||||
$data = $s->elem($element_number, $component_number)
|
||||
return the contents of a specified element and if specified
|
||||
component of that element
|
||||
|
||||
=head2 element
|
||||
|
||||
syntactic sugar this wraps the rlem method in a fuller name
|
||||
|
||||
=head2 as_string
|
||||
|
||||
returns a string representation of the segment
|
||||
|
||||
=head2 _parse_seg
|
||||
|
||||
passed a string representation of a segment returns a hash ref with
|
||||
separate tag and data elements
|
||||
|
||||
=head2 _get_elements
|
||||
|
||||
passed the data portion of a segment, splits it into elements, passing each to
|
||||
components to further parse them. Returns a reference to an array of
|
||||
elements
|
||||
|
||||
=head2 _components
|
||||
|
||||
Passed a string element splits it into components and returns a reference
|
||||
to an array of components, if only one component is present that is returned
|
||||
directly.
|
||||
quote characters are removed from the components
|
||||
|
||||
=head2 de_escape
|
||||
|
||||
Removes Edifact escapes from the passed string and returns the modified
|
||||
string
|
||||
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Colin Campbell <colin.campbell@ptfs-europe.com>
|
||||
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2014,2016, PTFS-Europe Ltd
|
||||
This program is free software, You may redistribute it under
|
||||
under the terms of the GNU General Public License
|
||||
|
||||
|
||||
=cut
|
||||
|
|
@ -0,0 +1,479 @@
|
|||
package Koha::Edifact::Transport;
|
||||
|
||||
# Copyright 2014,2015 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 3 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, see <http://www.gnu.org/licenses>.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
use DateTime;
|
||||
use Carp;
|
||||
use English qw{ -no_match_vars };
|
||||
use Net::FTP;
|
||||
use Net::SFTP::Foreign;
|
||||
use File::Slurp;
|
||||
use File::Copy;
|
||||
use File::Basename qw( fileparse );
|
||||
use File::Spec;
|
||||
use Koha::Database;
|
||||
use Encode qw( from_to );
|
||||
|
||||
sub new {
|
||||
my ( $class, $account_id ) = @_;
|
||||
my $database = Koha::Database->new();
|
||||
my $schema = $database->schema();
|
||||
my $acct = $schema->resultset('VendorEdiAccount')->find($account_id);
|
||||
my $self = {
|
||||
account => $acct,
|
||||
schema => $schema,
|
||||
working_dir => File::Spec->tmpdir(), #temporary work directory
|
||||
transfer_date => DateTime->now( time_zone => 'local' ),
|
||||
};
|
||||
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub working_directory {
|
||||
my ( $self, $new_value ) = @_;
|
||||
if ($new_value) {
|
||||
$self->{working_directory} = $new_value;
|
||||
}
|
||||
return $self->{working_directory};
|
||||
}
|
||||
|
||||
sub download_messages {
|
||||
my ( $self, $message_type ) = @_;
|
||||
$self->{message_type} = $message_type;
|
||||
|
||||
my @retrieved_files;
|
||||
|
||||
if ( $self->{account}->transport eq 'SFTP' ) {
|
||||
@retrieved_files = $self->sftp_download();
|
||||
}
|
||||
elsif ( $self->{account}->transport eq 'FILE' ) {
|
||||
@retrieved_files = $self->file_download();
|
||||
}
|
||||
else { # assume FTP
|
||||
@retrieved_files = $self->ftp_download();
|
||||
}
|
||||
return @retrieved_files;
|
||||
}
|
||||
|
||||
sub upload_messages {
|
||||
my ( $self, @messages ) = @_;
|
||||
if (@messages) {
|
||||
if ( $self->{account}->transport eq 'SFTP' ) {
|
||||
$self->sftp_upload(@messages);
|
||||
}
|
||||
elsif ( $self->{account}->transport eq 'FILE' ) {
|
||||
$self->file_upload(@messages);
|
||||
}
|
||||
else { # assume FTP
|
||||
$self->ftp_upload(@messages);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub file_download {
|
||||
my $self = shift;
|
||||
my @downloaded_files;
|
||||
|
||||
my $file_ext = _get_file_ext( $self->{message_type} );
|
||||
|
||||
my $dir = $self->{account}->download_directory; # makes code more readable
|
||||
# C = ready to retrieve E = Edifact
|
||||
my $msg_hash = $self->message_hash();
|
||||
if ( opendir my $dh, $dir ) {
|
||||
my @file_list = readdir $dh;
|
||||
closedir $dh;
|
||||
foreach my $filename (@file_list) {
|
||||
|
||||
if ( $filename =~ m/[.]$file_ext$/ ) {
|
||||
if ( copy( "$dir/$filename", $self->{working_dir} ) ) {
|
||||
}
|
||||
else {
|
||||
carp "copy of $filename failed";
|
||||
next;
|
||||
}
|
||||
push @downloaded_files, $filename;
|
||||
my $processed_name = $filename;
|
||||
substr $processed_name, -3, 1, 'E';
|
||||
move( "$dir/$filename", "$dir/$processed_name" );
|
||||
}
|
||||
}
|
||||
$self->ingest( $msg_hash, @downloaded_files );
|
||||
}
|
||||
else {
|
||||
carp "Cannot open $dir";
|
||||
return;
|
||||
}
|
||||
return @downloaded_files;
|
||||
}
|
||||
|
||||
sub sftp_download {
|
||||
my $self = shift;
|
||||
|
||||
my $file_ext = _get_file_ext( $self->{message_type} );
|
||||
|
||||
# C = ready to retrieve E = Edifact
|
||||
my $msg_hash = $self->message_hash();
|
||||
my @downloaded_files;
|
||||
my $sftp = Net::SFTP::Foreign->new(
|
||||
host => $self->{account}->host,
|
||||
user => $self->{account}->username,
|
||||
password => $self->{account}->password,
|
||||
timeout => 10,
|
||||
);
|
||||
if ( $sftp->error ) {
|
||||
return $self->_abort_download( undef,
|
||||
'Unable to connect to remote host: ' . $sftp->error );
|
||||
}
|
||||
$sftp->setcwd( $self->{account}->download_directory )
|
||||
or return $self->_abort_download( $sftp,
|
||||
"Cannot change remote dir : $sftp->error" );
|
||||
my $file_list = $sftp->ls()
|
||||
or return $self->_abort_download( $sftp,
|
||||
"cannot get file list from server: $sftp->error" );
|
||||
foreach my $file ( @{$file_list} ) {
|
||||
my $filename = $file->{filename};
|
||||
|
||||
if ( $filename =~ m/[.]$file_ext$/ ) {
|
||||
$sftp->get( $filename, "$self->{working_dir}/$filename" );
|
||||
if ( $sftp->error ) {
|
||||
$self->_abort_download( $sftp,
|
||||
"Error retrieving $filename: $sftp->error" );
|
||||
last;
|
||||
}
|
||||
push @downloaded_files, $filename;
|
||||
my $processed_name = $filename;
|
||||
substr $processed_name, -3, 1, 'E';
|
||||
|
||||
#$sftp->atomic_rename( $filename, $processed_name );
|
||||
my $ret = $sftp->rename( $filename, $processed_name );
|
||||
if ( !$ret ) {
|
||||
$self->_abort_download( $sftp,
|
||||
"Error renaming $filename: $sftp->error" );
|
||||
last;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
$sftp->disconnect;
|
||||
$self->ingest( $msg_hash, @downloaded_files );
|
||||
|
||||
return @downloaded_files;
|
||||
}
|
||||
|
||||
sub ingest {
|
||||
my ( $self, $msg_hash, @downloaded_files ) = @_;
|
||||
foreach my $f (@downloaded_files) {
|
||||
$msg_hash->{filename} = $f;
|
||||
my $file_content =
|
||||
read_file( "$self->{working_dir}/$f", binmode => ':raw' );
|
||||
if ( !defined $file_content ) {
|
||||
carp "Unable to read download file $f";
|
||||
next;
|
||||
}
|
||||
from_to( $file_content, 'iso-8859-1', 'utf8' );
|
||||
$msg_hash->{raw_msg} = $file_content;
|
||||
$self->{schema}->resultset('EdifactMessage')->create($msg_hash);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub ftp_download {
|
||||
my $self = shift;
|
||||
|
||||
my $file_ext = _get_file_ext( $self->{message_type} );
|
||||
|
||||
# C = ready to retrieve E = Edifact
|
||||
|
||||
my $msg_hash = $self->message_hash();
|
||||
my @downloaded_files;
|
||||
my $ftp = Net::FTP->new(
|
||||
$self->{account}->host,
|
||||
Timeout => 10,
|
||||
Passive => 1
|
||||
)
|
||||
or return $self->_abort_download( undef,
|
||||
"Cannot connect to $self->{account}->host: $EVAL_ERROR" );
|
||||
$ftp->login( $self->{account}->username, $self->{account}->password )
|
||||
or return $self->_abort_download( $ftp, "Cannot login: $ftp->message()" );
|
||||
$ftp->cwd( $self->{account}->download_directory )
|
||||
or return $self->_abort_download( $ftp,
|
||||
"Cannot change remote dir : $ftp->message()" );
|
||||
my $file_list = $ftp->ls()
|
||||
or
|
||||
return $self->_abort_download( $ftp, 'cannot get file list from server' );
|
||||
|
||||
foreach my $filename ( @{$file_list} ) {
|
||||
|
||||
if ( $filename =~ m/[.]$file_ext$/ ) {
|
||||
|
||||
if ( !$ftp->get( $filename, "$self->{working_dir}/$filename" ) ) {
|
||||
$self->_abort_download( $ftp,
|
||||
"Error retrieving $filename: $ftp->message" );
|
||||
last;
|
||||
}
|
||||
|
||||
push @downloaded_files, $filename;
|
||||
my $processed_name = $filename;
|
||||
substr $processed_name, -3, 1, 'E';
|
||||
$ftp->rename( $filename, $processed_name );
|
||||
}
|
||||
}
|
||||
$ftp->quit;
|
||||
|
||||
$self->ingest( $msg_hash, @downloaded_files );
|
||||
|
||||
return @downloaded_files;
|
||||
}
|
||||
|
||||
sub ftp_upload {
|
||||
my ( $self, @messages ) = @_;
|
||||
my $ftp = Net::FTP->new(
|
||||
$self->{account}->host,
|
||||
Timeout => 10,
|
||||
Passive => 1
|
||||
)
|
||||
or return $self->_abort_download( undef,
|
||||
"Cannot connect to $self->{account}->host: $EVAL_ERROR" );
|
||||
$ftp->login( $self->{account}->username, $self->{account}->password )
|
||||
or return $self->_abort_download( $ftp, "Cannot login: $ftp->message()" );
|
||||
$ftp->cwd( $self->{account}->upload_directory )
|
||||
or return $self->_abort_download( $ftp,
|
||||
"Cannot change remote dir : $ftp->message()" );
|
||||
foreach my $m (@messages) {
|
||||
my $content = $m->raw_msg;
|
||||
if ($content) {
|
||||
open my $fh, '<', \$content;
|
||||
if ( $ftp->put( $fh, $m->filename ) ) {
|
||||
close $fh;
|
||||
$m->transfer_date( $self->{transfer_date} );
|
||||
$m->status('sent');
|
||||
$m->update;
|
||||
}
|
||||
else {
|
||||
# error in transfer
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ftp->quit;
|
||||
return;
|
||||
}
|
||||
|
||||
sub sftp_upload {
|
||||
my ( $self, @messages ) = @_;
|
||||
my $sftp = Net::SFTP::Foreign->new(
|
||||
host => $self->{account}->host,
|
||||
user => $self->{account}->username,
|
||||
password => $self->{account}->password,
|
||||
timeout => 10,
|
||||
);
|
||||
$sftp->die_on_error("Cannot ssh to $self->{account}->host");
|
||||
$sftp->setcwd( $self->{account}->upload_directory )
|
||||
or return $self->_abort_download( $sftp,
|
||||
"Cannot change remote dir : $sftp->error" );
|
||||
foreach my $m (@messages) {
|
||||
my $content = $m->raw_msg;
|
||||
if ($content) {
|
||||
open my $fh, '<', \$content;
|
||||
if ( $sftp->put( $fh, $m->filename ) ) {
|
||||
close $fh;
|
||||
$m->transfer_date( $self->{transfer_date} );
|
||||
$m->status('sent');
|
||||
$m->update;
|
||||
}
|
||||
else {
|
||||
# error in transfer
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# sftp will be closed on object destructor
|
||||
return;
|
||||
}
|
||||
|
||||
sub file_upload {
|
||||
my ( $self, @messages ) = @_;
|
||||
my $dir = $self->{account}->upload_directory;
|
||||
if ( -d $dir ) {
|
||||
foreach my $m (@messages) {
|
||||
my $content = $m->raw_msg;
|
||||
if ($content) {
|
||||
my $filename = $m->filename;
|
||||
my $new_filename = "$dir/$filename";
|
||||
if ( open my $fh, '>', $new_filename ) {
|
||||
print {$fh} $content;
|
||||
close $fh;
|
||||
$m->transfer_date( $self->{transfer_date} );
|
||||
$m->status('sent');
|
||||
$m->update;
|
||||
}
|
||||
else {
|
||||
carp "Could not transfer $m->filename : $ERRNO";
|
||||
next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
carp "Upload directory $dir does not exist";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub _abort_download {
|
||||
my ( $self, $handle, $log_message ) = @_;
|
||||
|
||||
my $a = $self->{account}->description;
|
||||
|
||||
if ($handle) {
|
||||
$handle->abort();
|
||||
}
|
||||
$log_message .= ": $a";
|
||||
carp $log_message;
|
||||
|
||||
#returns undef i.e. an empty array
|
||||
return;
|
||||
}
|
||||
|
||||
sub _get_file_ext {
|
||||
my $type = shift;
|
||||
|
||||
# Extension format
|
||||
# 1st char Status C = Ready For pickup A = Completed E = Extracted
|
||||
# 2nd Char Standard E = Edifact
|
||||
# 3rd Char Type of message
|
||||
my %file_types = (
|
||||
QUOTE => 'CEQ',
|
||||
INVOICE => 'CEI',
|
||||
ORDRSP => 'CEA',
|
||||
ALL => 'CE.',
|
||||
);
|
||||
if ( exists $file_types{$type} ) {
|
||||
return $file_types{$type};
|
||||
}
|
||||
return 'XXXX'; # non matching type
|
||||
}
|
||||
|
||||
sub message_hash {
|
||||
my $self = shift;
|
||||
my $msg = {
|
||||
message_type => $self->{message_type},
|
||||
vendor_id => $self->{account}->vendor_id,
|
||||
edi_acct => $self->{account}->id,
|
||||
status => 'new',
|
||||
deleted => 0,
|
||||
transfer_date => $self->{transfer_date}->ymd(),
|
||||
};
|
||||
|
||||
return $msg;
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::Edifact::Transport
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my $download = Koha::Edifact::Transport->new( $vendor_edi_account_id );
|
||||
$downlowd->download_messages('QUOTE');
|
||||
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Module that handles Edifact download and upload transport
|
||||
currently can use sftp or ftp
|
||||
Or FILE to access a local directory (useful for testing)
|
||||
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 new
|
||||
|
||||
Creates an object of Edifact::Transport requires to be passed the id
|
||||
identifying the relevant edi vendor account
|
||||
|
||||
=head2 working_directory
|
||||
|
||||
getter and setter for the working_directory attribute
|
||||
|
||||
=head2 download_messages
|
||||
|
||||
called with the message type to download will perform the download
|
||||
using the appropriate transport method
|
||||
|
||||
=head2 upload_messages
|
||||
|
||||
passed an array of messages will upload them to the supplier site
|
||||
|
||||
=head2 sftp_download
|
||||
|
||||
called by download_messages to perform the download using SFTP
|
||||
|
||||
=head2 ingest
|
||||
|
||||
loads downloaded files into the database
|
||||
|
||||
=head2 ftp_download
|
||||
|
||||
called by download_messages to perform the download using FTP
|
||||
|
||||
=head2 ftp_upload
|
||||
|
||||
called by upload_messages to perform the upload using ftp
|
||||
|
||||
=head2 sftp_upload
|
||||
|
||||
called by upload_messages to perform the upload using sftp
|
||||
|
||||
=head2 _abort_download
|
||||
|
||||
internal routine to halt operation on error and supply a stacktrace
|
||||
|
||||
=head2 _get_file_ext
|
||||
|
||||
internal method returning standard suffix for file names
|
||||
according to message type
|
||||
|
||||
=head2 set_transport_direct
|
||||
|
||||
sets the direct ingest flag so that the object reads files from
|
||||
the local file system useful in debugging
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Colin Campbell <colin.campbell@ptfs-europe.com>
|
||||
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2014,2015 PTFS-Europe Ltd
|
||||
This program is free software, You may redistribute it under
|
||||
under the terms of the GNU General Public License
|
||||
|
||||
|
||||
=cut
|
||||
|
|
@ -263,6 +263,21 @@ __PACKAGE__->belongs_to(
|
|||
},
|
||||
);
|
||||
|
||||
=head2 edifact_messages
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Koha::Schema::Result::EdifactMessage>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"edifact_messages",
|
||||
"Koha::Schema::Result::EdifactMessage",
|
||||
{ "foreign.basketno" => "self.basketno" },
|
||||
{ cascade_copy => 0, cascade_delete => 0 },
|
||||
);
|
||||
|
||||
=head2 borrowernumbers
|
||||
|
||||
Type: many_to_many
|
||||
|
|
@ -274,8 +289,8 @@ Composing rels: L</aqbasketusers> -> borrowernumber
|
|||
__PACKAGE__->many_to_many("borrowernumbers", "aqbasketusers", "borrowernumber");
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07039 @ 2014-07-11 09:26:55
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:pT+YFf9nfD/dmBuE4RNCFw
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2014-09-02 11:37:47
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:tsMzwP7eofOR27sfZSTqFQ
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
|||
|
|
@ -311,6 +311,21 @@ __PACKAGE__->has_many(
|
|||
{ cascade_copy => 0, cascade_delete => 0 },
|
||||
);
|
||||
|
||||
=head2 edifact_messages
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Koha::Schema::Result::EdifactMessage>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"edifact_messages",
|
||||
"Koha::Schema::Result::EdifactMessage",
|
||||
{ "foreign.vendor_id" => "self.id" },
|
||||
{ cascade_copy => 0, cascade_delete => 0 },
|
||||
);
|
||||
|
||||
=head2 invoiceprice
|
||||
|
||||
Type: belongs_to
|
||||
|
|
@ -351,10 +366,25 @@ __PACKAGE__->belongs_to(
|
|||
},
|
||||
);
|
||||
|
||||
=head2 vendor_edi_accounts
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07042 @ 2016-03-09 15:14:35
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:JPswQh/s5S4nZnUzMckLnw
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Koha::Schema::Result::VendorEdiAccount>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"vendor_edi_accounts",
|
||||
"Koha::Schema::Result::VendorEdiAccount",
|
||||
{ "foreign.vendor_id" => "self.id" },
|
||||
{ cascade_copy => 0, cascade_delete => 0 },
|
||||
);
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
# Created by DBIx::Class::Schema::Loader v0.07042 @ 2016-03-10 19:36:24
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:mNH0CKfuRQqoOLXieV43DQ
|
||||
|
||||
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -257,6 +257,21 @@ __PACKAGE__->has_many(
|
|||
{ cascade_copy => 0, cascade_delete => 0 },
|
||||
);
|
||||
|
||||
=head2 vendor_edi_accounts
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Koha::Schema::Result::VendorEdiAccount>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"vendor_edi_accounts",
|
||||
"Koha::Schema::Result::VendorEdiAccount",
|
||||
{ "foreign.shipment_budget" => "self.budget_id" },
|
||||
{ cascade_copy => 0, cascade_delete => 0 },
|
||||
);
|
||||
|
||||
=head2 borrowernumbers
|
||||
|
||||
Type: many_to_many
|
||||
|
|
@ -268,8 +283,8 @@ Composing rels: L</aqbudgetborrowers> -> borrowernumber
|
|||
__PACKAGE__->many_to_many("borrowernumbers", "aqbudgetborrowers", "borrowernumber");
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07039 @ 2015-02-09 15:51:54
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:SZKnWPCMNFUm/TzeBxeDZA
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2015-03-04 10:26:49
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:E4J/D0+2j0/8JZd0YRnoeA
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
|||
|
|
@ -70,6 +70,12 @@ __PACKAGE__->table("aqinvoices");
|
|||
is_foreign_key: 1
|
||||
is_nullable: 1
|
||||
|
||||
=head2 message_id
|
||||
|
||||
data_type: 'integer'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 1
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
|
|
@ -89,6 +95,8 @@ __PACKAGE__->add_columns(
|
|||
{ data_type => "decimal", is_nullable => 1, size => [28, 6] },
|
||||
"shipmentcost_budgetid",
|
||||
{ data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
|
||||
"message_id",
|
||||
{ data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
|
@ -135,6 +143,26 @@ __PACKAGE__->belongs_to(
|
|||
{ is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
|
||||
);
|
||||
|
||||
=head2 message
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Koha::Schema::Result::EdifactMessage>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"message",
|
||||
"Koha::Schema::Result::EdifactMessage",
|
||||
{ id => "message_id" },
|
||||
{
|
||||
is_deferrable => 1,
|
||||
join_type => "LEFT",
|
||||
on_delete => "SET NULL",
|
||||
on_update => "RESTRICT",
|
||||
},
|
||||
);
|
||||
|
||||
=head2 shipmentcost_budgetid
|
||||
|
||||
Type: belongs_to
|
||||
|
|
@ -156,8 +184,8 @@ __PACKAGE__->belongs_to(
|
|||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07039 @ 2014-07-11 09:26:55
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:3se4f767VfvBKaZ8tlXwHQ
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2014-09-18 16:21:46
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:FPZXlNt8dkjhgt2Rtc+krQ
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
|||
|
|
@ -228,6 +228,29 @@ __PACKAGE__->table("aqorders");
|
|||
is_nullable: 1
|
||||
size: 16
|
||||
|
||||
=head2 line_item_id
|
||||
|
||||
data_type: 'varchar'
|
||||
is_nullable: 1
|
||||
size: 35
|
||||
|
||||
=head2 suppliers_reference_number
|
||||
|
||||
data_type: 'varchar'
|
||||
is_nullable: 1
|
||||
size: 35
|
||||
|
||||
=head2 suppliers_reference_qualifier
|
||||
|
||||
data_type: 'varchar'
|
||||
is_nullable: 1
|
||||
size: 3
|
||||
|
||||
=head2 suppliers_report
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
|
|
@ -311,6 +334,14 @@ __PACKAGE__->add_columns(
|
|||
is_nullable => 1,
|
||||
size => 16,
|
||||
},
|
||||
"line_item_id",
|
||||
{ data_type => "varchar", is_nullable => 1, size => 35 },
|
||||
"suppliers_reference_number",
|
||||
{ data_type => "varchar", is_nullable => 1, size => 35 },
|
||||
"suppliers_reference_qualifier",
|
||||
{ data_type => "varchar", is_nullable => 1, size => 3 },
|
||||
"suppliers_report",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
|
@ -513,9 +544,9 @@ Composing rels: L</aqorder_users> -> borrowernumber
|
|||
__PACKAGE__->many_to_many("borrowernumbers", "aqorder_users", "borrowernumber");
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07042 @ 2016-03-09 15:14:35
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:somjoqKl7W2FYfhmgw4LQQ
|
||||
# Created by DBIx::Class::Schema::Loader v0.07042 @ 2016-03-10 19:38:20
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:2kQhxUE0pZ3PpwOqGtvB+g
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -397,6 +397,21 @@ __PACKAGE__->might_have(
|
|||
{ cascade_copy => 0, cascade_delete => 0 },
|
||||
);
|
||||
|
||||
=head2 edifact_eans
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Koha::Schema::Result::EdifactEan>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"edifact_eans",
|
||||
"Koha::Schema::Result::EdifactEan",
|
||||
{ "foreign.branchcode" => "self.branchcode" },
|
||||
{ cascade_copy => 0, cascade_delete => 0 },
|
||||
);
|
||||
|
||||
=head2 hold_fill_targets
|
||||
|
||||
Type: has_many
|
||||
|
|
@ -513,9 +528,9 @@ Composing rels: L</branchrelations> -> categorycode
|
|||
__PACKAGE__->many_to_many("categorycodes", "branchrelations", "categorycode");
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07039 @ 2014-11-06 15:26:36
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CGNPB/MkGLOihDThj43/4A
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2014-11-26 11:08:29
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:FjNI9OEpa5OKfwwCkggu0w
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
use utf8;
|
||||
package Koha::Schema::Result::EdifactEan;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::Schema::Result::EdifactEan
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
=head1 TABLE: C<edifact_ean>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("edifact_ean");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 ee_id
|
||||
|
||||
data_type: 'integer'
|
||||
extra: {unsigned => 1}
|
||||
is_auto_increment: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 branchcode
|
||||
|
||||
data_type: 'varchar'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
size: 10
|
||||
|
||||
=head2 ean
|
||||
|
||||
data_type: 'varchar'
|
||||
is_nullable: 0
|
||||
size: 15
|
||||
|
||||
=head2 id_code_qualifier
|
||||
|
||||
data_type: 'varchar'
|
||||
default_value: 14
|
||||
is_nullable: 0
|
||||
size: 3
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"ee_id",
|
||||
{
|
||||
data_type => "integer",
|
||||
extra => { unsigned => 1 },
|
||||
is_auto_increment => 1,
|
||||
is_nullable => 0,
|
||||
},
|
||||
"branchcode",
|
||||
{ data_type => "varchar", is_foreign_key => 1, is_nullable => 0, size => 10 },
|
||||
"ean",
|
||||
{ data_type => "varchar", is_nullable => 0, size => 15 },
|
||||
"id_code_qualifier",
|
||||
{ data_type => "varchar", default_value => 14, is_nullable => 0, size => 3 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</ee_id>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->set_primary_key("ee_id");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 branchcode
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Koha::Schema::Result::Branch>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"branchcode",
|
||||
"Koha::Schema::Result::Branch",
|
||||
{ branchcode => "branchcode" },
|
||||
{ is_deferrable => 1, on_delete => "RESTRICT", on_update => "RESTRICT" },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07042 @ 2015-06-12 10:22:04
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:WWcMBSXeuzgCPqM0KMxfBg
|
||||
|
||||
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
__PACKAGE__->belongs_to('branch',
|
||||
"Koha::Schema::Result::Branch",
|
||||
{ 'branchcode' => 'branchcode' },
|
||||
{
|
||||
is_deferrable => 1,
|
||||
join_type => 'LEFT',
|
||||
on_delete => 'CASCADE',
|
||||
on_update => 'CASCADE',
|
||||
},
|
||||
);
|
||||
|
||||
1;
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
use utf8;
|
||||
package Koha::Schema::Result::EdifactMessage;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::Schema::Result::EdifactMessage
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
=head1 TABLE: C<edifact_messages>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("edifact_messages");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 id
|
||||
|
||||
data_type: 'integer'
|
||||
is_auto_increment: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 message_type
|
||||
|
||||
data_type: 'varchar'
|
||||
is_nullable: 0
|
||||
size: 10
|
||||
|
||||
=head2 transfer_date
|
||||
|
||||
data_type: 'date'
|
||||
datetime_undef_if_invalid: 1
|
||||
is_nullable: 1
|
||||
|
||||
=head2 vendor_id
|
||||
|
||||
data_type: 'integer'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 1
|
||||
|
||||
=head2 edi_acct
|
||||
|
||||
data_type: 'integer'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 1
|
||||
|
||||
=head2 status
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 basketno
|
||||
|
||||
data_type: 'integer'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 1
|
||||
|
||||
=head2 raw_msg
|
||||
|
||||
data_type: 'mediumtext'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 filename
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 deleted
|
||||
|
||||
data_type: 'tinyint'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"id",
|
||||
{ data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
|
||||
"message_type",
|
||||
{ data_type => "varchar", is_nullable => 0, size => 10 },
|
||||
"transfer_date",
|
||||
{ data_type => "date", datetime_undef_if_invalid => 1, is_nullable => 1 },
|
||||
"vendor_id",
|
||||
{ data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
|
||||
"edi_acct",
|
||||
{ data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
|
||||
"status",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"basketno",
|
||||
{ data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
|
||||
"raw_msg",
|
||||
{ data_type => "mediumtext", is_nullable => 1 },
|
||||
"filename",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"deleted",
|
||||
{ data_type => "tinyint", default_value => 0, is_nullable => 0 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</id>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->set_primary_key("id");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 aqinvoices
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Koha::Schema::Result::Aqinvoice>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"aqinvoices",
|
||||
"Koha::Schema::Result::Aqinvoice",
|
||||
{ "foreign.message_id" => "self.id" },
|
||||
{ cascade_copy => 0, cascade_delete => 0 },
|
||||
);
|
||||
|
||||
=head2 basketno
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Koha::Schema::Result::Aqbasket>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"basketno",
|
||||
"Koha::Schema::Result::Aqbasket",
|
||||
{ basketno => "basketno" },
|
||||
{
|
||||
is_deferrable => 1,
|
||||
join_type => "LEFT",
|
||||
on_delete => "RESTRICT",
|
||||
on_update => "RESTRICT",
|
||||
},
|
||||
);
|
||||
|
||||
=head2 edi_acct
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Koha::Schema::Result::VendorEdiAccount>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"edi_acct",
|
||||
"Koha::Schema::Result::VendorEdiAccount",
|
||||
{ id => "edi_acct" },
|
||||
{
|
||||
is_deferrable => 1,
|
||||
join_type => "LEFT",
|
||||
on_delete => "RESTRICT",
|
||||
on_update => "RESTRICT",
|
||||
},
|
||||
);
|
||||
|
||||
=head2 vendor
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Koha::Schema::Result::Aqbookseller>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"vendor",
|
||||
"Koha::Schema::Result::Aqbookseller",
|
||||
{ id => "vendor_id" },
|
||||
{
|
||||
is_deferrable => 1,
|
||||
join_type => "LEFT",
|
||||
on_delete => "RESTRICT",
|
||||
on_update => "RESTRICT",
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2015-02-25 10:41:36
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:l4h8AsG2RJupxXQcEw8yzQ
|
||||
|
||||
|
||||
1;
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
use utf8;
|
||||
package Koha::Schema::Result::MsgInvoice;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::Schema::Result::MsgInvoice
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
=head1 TABLE: C<msg_invoice>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("msg_invoice");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 mi_id
|
||||
|
||||
data_type: 'integer'
|
||||
is_auto_increment: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 msg_id
|
||||
|
||||
data_type: 'integer'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 1
|
||||
|
||||
=head2 invoiceid
|
||||
|
||||
data_type: 'integer'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 1
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"mi_id",
|
||||
{ data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
|
||||
"msg_id",
|
||||
{ data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
|
||||
"invoiceid",
|
||||
{ data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</mi_id>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->set_primary_key("mi_id");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 invoiceid
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Koha::Schema::Result::Aqinvoice>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"invoiceid",
|
||||
"Koha::Schema::Result::Aqinvoice",
|
||||
{ invoiceid => "invoiceid" },
|
||||
{
|
||||
is_deferrable => 1,
|
||||
join_type => "LEFT",
|
||||
on_delete => "RESTRICT",
|
||||
on_update => "RESTRICT",
|
||||
},
|
||||
);
|
||||
|
||||
=head2 msg
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Koha::Schema::Result::EdifactMessage>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"msg",
|
||||
"Koha::Schema::Result::EdifactMessage",
|
||||
{ id => "msg_id" },
|
||||
{
|
||||
is_deferrable => 1,
|
||||
join_type => "LEFT",
|
||||
on_delete => "RESTRICT",
|
||||
on_update => "RESTRICT",
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2014-09-02 11:37:47
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:F1jqlEH57dpxn2Pvm/vPGA
|
||||
|
||||
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
1;
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
use utf8;
|
||||
package Koha::Schema::Result::VendorEdiAccount;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::Schema::Result::VendorEdiAccount
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
=head1 TABLE: C<vendor_edi_accounts>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("vendor_edi_accounts");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 id
|
||||
|
||||
data_type: 'integer'
|
||||
is_auto_increment: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 description
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 0
|
||||
|
||||
=head2 host
|
||||
|
||||
data_type: 'varchar'
|
||||
is_nullable: 1
|
||||
size: 40
|
||||
|
||||
=head2 username
|
||||
|
||||
data_type: 'varchar'
|
||||
is_nullable: 1
|
||||
size: 40
|
||||
|
||||
=head2 password
|
||||
|
||||
data_type: 'varchar'
|
||||
is_nullable: 1
|
||||
size: 40
|
||||
|
||||
=head2 last_activity
|
||||
|
||||
data_type: 'date'
|
||||
datetime_undef_if_invalid: 1
|
||||
is_nullable: 1
|
||||
|
||||
=head2 vendor_id
|
||||
|
||||
data_type: 'integer'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 1
|
||||
|
||||
=head2 download_directory
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 upload_directory
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 san
|
||||
|
||||
data_type: 'varchar'
|
||||
is_nullable: 1
|
||||
size: 20
|
||||
|
||||
=head2 id_code_qualifier
|
||||
|
||||
data_type: 'varchar'
|
||||
default_value: 14
|
||||
is_nullable: 1
|
||||
size: 3
|
||||
|
||||
=head2 transport
|
||||
|
||||
data_type: 'varchar'
|
||||
default_value: 'FTP'
|
||||
is_nullable: 1
|
||||
size: 6
|
||||
|
||||
=head2 quotes_enabled
|
||||
|
||||
data_type: 'tinyint'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 invoices_enabled
|
||||
|
||||
data_type: 'tinyint'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 orders_enabled
|
||||
|
||||
data_type: 'tinyint'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 responses_enabled
|
||||
|
||||
data_type: 'tinyint'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 auto_orders
|
||||
|
||||
data_type: 'tinyint'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 shipment_budget
|
||||
|
||||
data_type: 'integer'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 1
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"id",
|
||||
{ data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
|
||||
"description",
|
||||
{ data_type => "text", is_nullable => 0 },
|
||||
"host",
|
||||
{ data_type => "varchar", is_nullable => 1, size => 40 },
|
||||
"username",
|
||||
{ data_type => "varchar", is_nullable => 1, size => 40 },
|
||||
"password",
|
||||
{ data_type => "varchar", is_nullable => 1, size => 40 },
|
||||
"last_activity",
|
||||
{ data_type => "date", datetime_undef_if_invalid => 1, is_nullable => 1 },
|
||||
"vendor_id",
|
||||
{ data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
|
||||
"download_directory",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"upload_directory",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"san",
|
||||
{ data_type => "varchar", is_nullable => 1, size => 20 },
|
||||
"id_code_qualifier",
|
||||
{ data_type => "varchar", default_value => 14, is_nullable => 1, size => 3 },
|
||||
"transport",
|
||||
{ data_type => "varchar", default_value => "FTP", is_nullable => 1, size => 6 },
|
||||
"quotes_enabled",
|
||||
{ data_type => "tinyint", default_value => 0, is_nullable => 0 },
|
||||
"invoices_enabled",
|
||||
{ data_type => "tinyint", default_value => 0, is_nullable => 0 },
|
||||
"orders_enabled",
|
||||
{ data_type => "tinyint", default_value => 0, is_nullable => 0 },
|
||||
"responses_enabled",
|
||||
{ data_type => "tinyint", default_value => 0, is_nullable => 0 },
|
||||
"auto_orders",
|
||||
{ data_type => "tinyint", default_value => 0, is_nullable => 0 },
|
||||
"shipment_budget",
|
||||
{ data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</id>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->set_primary_key("id");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 edifact_messages
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Koha::Schema::Result::EdifactMessage>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"edifact_messages",
|
||||
"Koha::Schema::Result::EdifactMessage",
|
||||
{ "foreign.edi_acct" => "self.id" },
|
||||
{ cascade_copy => 0, cascade_delete => 0 },
|
||||
);
|
||||
|
||||
=head2 shipment_budget
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Koha::Schema::Result::Aqbudget>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"shipment_budget",
|
||||
"Koha::Schema::Result::Aqbudget",
|
||||
{ budget_id => "shipment_budget" },
|
||||
{
|
||||
is_deferrable => 1,
|
||||
join_type => "LEFT",
|
||||
on_delete => "RESTRICT",
|
||||
on_update => "RESTRICT",
|
||||
},
|
||||
);
|
||||
|
||||
=head2 vendor
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Koha::Schema::Result::Aqbookseller>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"vendor",
|
||||
"Koha::Schema::Result::Aqbookseller",
|
||||
{ id => "vendor_id" },
|
||||
{
|
||||
is_deferrable => 1,
|
||||
join_type => "LEFT",
|
||||
on_delete => "RESTRICT",
|
||||
on_update => "RESTRICT",
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07042 @ 2015-08-19 11:41:15
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:0CgJuFAItI71dfSG88NWhg
|
||||
|
||||
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
1;
|
||||
|
|
@ -36,6 +36,8 @@ 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 Koha::Database;
|
||||
use Koha::EDI qw( create_edi_order get_edifact_ean );
|
||||
|
||||
=head1 NAME
|
||||
|
||||
|
|
@ -67,6 +69,7 @@ the supplier this script have to display the basket.
|
|||
|
||||
my $query = new CGI;
|
||||
our $basketno = $query->param('basketno');
|
||||
my $ean = $query->param('ean');
|
||||
my $booksellerid = $query->param('booksellerid');
|
||||
my $duplinbatch = $query->param('duplinbatch');
|
||||
|
||||
|
|
@ -84,6 +87,10 @@ my ( $template, $loggedinuser, $cookie, $userflags ) = get_template_and_user(
|
|||
my $basket = GetBasket($basketno);
|
||||
$booksellerid = $basket->{booksellerid} unless $booksellerid;
|
||||
my $bookseller = Koha::Acquisition::Bookseller->fetch({ id => $booksellerid });
|
||||
my $schema = Koha::Database->new()->schema();
|
||||
my $rs = $schema->resultset('VendorEdiAccount')->search(
|
||||
{ vendor_id => $booksellerid, } );
|
||||
$template->param( ediaccount => ($rs->count > 0));
|
||||
|
||||
unless (CanUserManageBasket($loggedinuser, $basket, $userflags)) {
|
||||
$template->param(
|
||||
|
|
@ -196,6 +203,9 @@ if ( $op eq 'delete_confirm' ) {
|
|||
} elsif ($op eq 'reopen') {
|
||||
ReopenBasket($query->param('basketno'));
|
||||
print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
|
||||
}
|
||||
elsif ( $op eq 'ediorder' ) {
|
||||
edi_close_and_order()
|
||||
} elsif ( $op eq 'mod_users' ) {
|
||||
my $basketusers_ids = $query->param('users_ids');
|
||||
my @basketusers = split( /:/, $basketusers_ids );
|
||||
|
|
@ -468,3 +478,70 @@ sub get_order_infos {
|
|||
}
|
||||
|
||||
output_html_with_http_headers $query, $cookie, $template->output;
|
||||
|
||||
|
||||
sub edi_close_and_order {
|
||||
my $confirm = $query->param('confirm') || $confirm_pref eq '2';
|
||||
if ($confirm) {
|
||||
my $edi_params = {
|
||||
basketno => $basketno,
|
||||
ean => $ean,
|
||||
};
|
||||
if ( $basket->{branch} ) {
|
||||
$edi_params->{branchcode} = $basket->{branch};
|
||||
}
|
||||
if ( create_edi_order($edi_params) ) {
|
||||
#$template->param( edifile => 1 );
|
||||
}
|
||||
CloseBasket($basketno);
|
||||
|
||||
# if requested, create basket group, close it and attach the basket
|
||||
if ( $query->param('createbasketgroup') ) {
|
||||
my $branchcode;
|
||||
if ( C4::Context->userenv
|
||||
and C4::Context->userenv->{'branch'}
|
||||
and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET" )
|
||||
{
|
||||
$branchcode = C4::Context->userenv->{'branch'};
|
||||
}
|
||||
my $basketgroupid = NewBasketgroup(
|
||||
{
|
||||
name => $basket->{basketname},
|
||||
booksellerid => $booksellerid,
|
||||
deliveryplace => $branchcode,
|
||||
billingplace => $branchcode,
|
||||
closed => 1,
|
||||
}
|
||||
);
|
||||
ModBasket(
|
||||
{
|
||||
basketno => $basketno,
|
||||
basketgroupid => $basketgroupid
|
||||
}
|
||||
);
|
||||
print $query->redirect(
|
||||
"/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=$booksellerid&closed=1"
|
||||
);
|
||||
}
|
||||
else {
|
||||
print $query->redirect(
|
||||
"/cgi-bin/koha/acqui/booksellers.pl?booksellerid=$booksellerid"
|
||||
);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
else {
|
||||
$template->param(
|
||||
edi_confirm => 1,
|
||||
booksellerid => $booksellerid,
|
||||
basketno => $basket->{basketno},
|
||||
basketname => $basket->{basketname},
|
||||
basketgroupname => $basket->{basketname},
|
||||
);
|
||||
if ($ean) {
|
||||
$template->param( ean => $ean );
|
||||
}
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ use CGI qw ( -utf8 );
|
|||
use C4::Acquisition qw/CloseBasketgroup ReOpenBasketgroup GetOrders GetBasketsByBasketgroup GetBasketsByBookseller ModBasketgroup NewBasketgroup DelBasketgroup GetBasketgroups ModBasket GetBasketgroup GetBasket GetBasketGroupAsCSV/;
|
||||
use C4::Branch qw/GetBranches/;
|
||||
use C4::Members qw/GetMember/;
|
||||
use Koha::EDI qw/create_edi_order get_edifact_ean/;
|
||||
|
||||
use Koha::Acquisition::Bookseller;
|
||||
|
||||
|
|
@ -206,12 +207,24 @@ sub printbasketgrouppdf{
|
|||
|
||||
}
|
||||
|
||||
sub generate_edifact_orders {
|
||||
my $basketgroupid = shift;
|
||||
my $baskets = GetBasketsByBasketgroup($basketgroupid);
|
||||
my $ean = get_edifact_ean();
|
||||
|
||||
for my $basket ( @{$baskets} ) {
|
||||
create_edi_order( { ean => $ean, basketno => $basket->{basketno}, } );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
my $op = $input->param('op') || 'display';
|
||||
# possible values of $op :
|
||||
# - add : adds a new basketgroup, or edit an open basketgroup, or display a closed basketgroup
|
||||
# - mod_basket : modify an individual basket of the basketgroup
|
||||
# - closeandprint : close and print an closed basketgroup in pdf. called by clicking on "Close and print" button in closed basketgroups list
|
||||
# - print : print a closed basketgroup. called by clicking on "Print" button in closed basketgroups list
|
||||
# - ediprint : generate edi order messages for the baskets in the group
|
||||
# - export : export in CSV a closed basketgroup. called by clicking on "Export" button in closed basketgroups list
|
||||
# - delete : delete an open basketgroup. called by clicking on "Delete" button in open basketgroups list
|
||||
# - reopen : reopen a closed basketgroup. called by clicking on "Reopen" button in closed basketgroup list
|
||||
|
|
@ -370,6 +383,10 @@ if ( $op eq "add" ) {
|
|||
$redirectpath .= "&listclosed=1" if $closedbg ;
|
||||
print $input->redirect($redirectpath );
|
||||
|
||||
} elsif ( $op eq 'ediprint') {
|
||||
my $basketgroupid = $input->param('basketgroupid');
|
||||
generate_edifact_orders( $basketgroupid );
|
||||
exit;
|
||||
}else{
|
||||
# no param : display the list of all basketgroups for a given vendor
|
||||
my $basketgroups = &GetBasketgroups($booksellerid);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
#!/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 3 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.
|
||||
|
||||
# This is an awkward construct and should probably be totally replaced
|
||||
# but as all sites so far are single ordering ean its not clear what we should
|
||||
# replace it with
|
||||
#
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use C4::Auth;
|
||||
use C4::Koha;
|
||||
use C4::Output;
|
||||
use Koha::Database;
|
||||
use CGI;
|
||||
my $schema = Koha::Database->new()->schema();
|
||||
|
||||
my @eans = $schema->resultset('EdifactEan')->search(
|
||||
{},
|
||||
{
|
||||
join => 'branch',
|
||||
}
|
||||
);
|
||||
my $query = CGI->new();
|
||||
my $basketno = $query->param('basketno');
|
||||
|
||||
if ( @eans == 1 ) {
|
||||
my $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 );
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Copyright 2014 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 3 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 Koha::Database;
|
||||
use C4::Koha;
|
||||
use C4::Auth;
|
||||
use C4::Output;
|
||||
|
||||
my $q = CGI->new;
|
||||
my ( $template, $loggedinuser, $cookie, $userflags ) = get_template_and_user(
|
||||
{
|
||||
template_name => 'acqui/edifactmsgs.tt',
|
||||
query => $q,
|
||||
type => 'intranet',
|
||||
authnotrequired => 0,
|
||||
flagsrequired => { acquisition => 'manage_edi' },
|
||||
debug => 1,
|
||||
}
|
||||
);
|
||||
|
||||
my $schema = Koha::Database->new()->schema();
|
||||
my $cmd = $q->param('op');
|
||||
if ( $cmd && $cmd == 'delete' ) {
|
||||
my $id = $q->param->('message_id');
|
||||
my $msg = $schema->resultset('EdifactMessage')->find($id);
|
||||
$msg->deleted(1);
|
||||
$msg->update;
|
||||
}
|
||||
|
||||
my @msgs = $schema->resultset('EdifactMessage')->search(
|
||||
{
|
||||
deleted => 0,
|
||||
},
|
||||
{
|
||||
join => 'vendor',
|
||||
order_by => { -desc => 'transfer_date' },
|
||||
}
|
||||
|
||||
)->all;
|
||||
|
||||
$template->param( messages => \@msgs );
|
||||
|
||||
output_html_with_http_headers( $q, $cookie, $template->output );
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Copyright 2014 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 3 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 Koha::Database;
|
||||
use C4::Koha;
|
||||
use C4::Auth;
|
||||
use C4::Output;
|
||||
|
||||
my $q = CGI->new;
|
||||
my ( $template, $loggedinuser, $cookie, $userflags ) = get_template_and_user(
|
||||
{
|
||||
template_name => 'acqui/edimsg.tt',
|
||||
query => $q,
|
||||
type => 'intranet',
|
||||
authnotrequired => 0,
|
||||
flagsrequired => { acquisition => 'manage_edi' },
|
||||
debug => 1,
|
||||
}
|
||||
);
|
||||
my $msg_id = $q->param('id');
|
||||
my $schema = Koha::Database->new()->schema();
|
||||
|
||||
my $msg = $schema->resultset('EdifactMessage')->find($msg_id);
|
||||
if ($msg) {
|
||||
my $transmission = $msg->raw_msg;
|
||||
|
||||
my @segments = segmentize($transmission);
|
||||
$template->param( segments => \@segments );
|
||||
}
|
||||
else {
|
||||
$template->param( no_message => 1 );
|
||||
}
|
||||
|
||||
output_html_with_http_headers( $q, $cookie, $template->output );
|
||||
|
||||
sub segmentize {
|
||||
my $raw = shift;
|
||||
|
||||
my $re = qr{
|
||||
(?> # dont backtrack into this group
|
||||
[?]. # either the escape character
|
||||
# followed by any other character
|
||||
| # or
|
||||
[^'?] # a character that is neither escape
|
||||
# nor split
|
||||
)+
|
||||
}x;
|
||||
my @segmented;
|
||||
while ( $raw =~ /($re)/g ) {
|
||||
push @segmented, "$1'";
|
||||
}
|
||||
return @segmented;
|
||||
}
|
||||
|
|
@ -62,6 +62,7 @@ my $author = $input->param('author');
|
|||
my $publisher = $input->param('publisher');
|
||||
my $publicationyear = $input->param('publicationyear');
|
||||
my $branch = $input->param('branch');
|
||||
my $message_id = $input->param('message_id');
|
||||
my $op = $input->param('op');
|
||||
|
||||
$shipmentdatefrom and $shipmentdatefrom = eval { dt_from_string( $shipmentdatefrom ) };
|
||||
|
|
@ -83,7 +84,8 @@ if ( $op and $op eq 'do_search' ) {
|
|||
author => $author,
|
||||
publisher => $publisher,
|
||||
publicationyear => $publicationyear,
|
||||
branchcode => $branch
|
||||
branchcode => $branch,
|
||||
message_id => $message_id,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,155 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Copyright 2011,2014 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 3 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 Koha::Database;
|
||||
|
||||
my $input = CGI->new();
|
||||
|
||||
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
|
||||
{
|
||||
template_name => 'admin/edi_accounts.tt',
|
||||
query => $input,
|
||||
type => 'intranet',
|
||||
authnotrequired => 0,
|
||||
flagsrequired => { acquisition => 'edi_manage' },
|
||||
}
|
||||
);
|
||||
|
||||
my $op = $input->param('op');
|
||||
$op ||= 'display';
|
||||
my $schema = Koha::Database->new()->schema();
|
||||
|
||||
if ( $op eq 'acct_form' ) {
|
||||
show_account();
|
||||
$template->param( acct_form => 1 );
|
||||
my @vendors = $schema->resultset('Aqbookseller')->search(
|
||||
undef,
|
||||
{
|
||||
columns => [ 'name', 'id' ],
|
||||
order_by => { -asc => 'name' }
|
||||
}
|
||||
);
|
||||
$template->param( vendors => \@vendors );
|
||||
$template->param(
|
||||
code_qualifiers => [
|
||||
{
|
||||
code => '14',
|
||||
description => 'EAN International',
|
||||
},
|
||||
{
|
||||
code => '31B',
|
||||
description => 'US SAN Agency',
|
||||
},
|
||||
{
|
||||
code => '91',
|
||||
description => 'Assigned by supplier',
|
||||
},
|
||||
{
|
||||
code => '92',
|
||||
description => 'Assigned by buyer',
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
}
|
||||
elsif ( $op eq 'delete_confirm' ) {
|
||||
show_account();
|
||||
$template->param( delete_confirm => 1 );
|
||||
}
|
||||
else {
|
||||
if ( $op eq 'save' ) {
|
||||
|
||||
# validate & display
|
||||
my $id = $input->param('id');
|
||||
my $fields = {
|
||||
description => $input->param('description'),
|
||||
host => $input->param('host'),
|
||||
username => $input->param('username'),
|
||||
password => $input->param('password'),
|
||||
vendor_id => $input->param('vendor_id'),
|
||||
upload_directory => $input->param('upload_directory'),
|
||||
download_directory => $input->param('download_directory'),
|
||||
san => $input->param('san'),
|
||||
transport => $input->param('transport'),
|
||||
quotes_enabled => defined $input->param('quotes_enabled'),
|
||||
invoices_enabled => defined $input->param('invoices_enabled'),
|
||||
orders_enabled => defined $input->param('orders_enabled'),
|
||||
responses_enabled => defined $input->param('responses_enabled'),
|
||||
auto_orders => defined $input->param('auto_orders'),
|
||||
id_code_qualifier => $input->param('id_code_qualifier'),
|
||||
};
|
||||
|
||||
if ($id) {
|
||||
$schema->resultset('VendorEdiAccount')->search(
|
||||
{
|
||||
id => $id,
|
||||
}
|
||||
)->update_all($fields);
|
||||
}
|
||||
else { # new record
|
||||
$schema->resultset('VendorEdiAccount')->create($fields);
|
||||
}
|
||||
}
|
||||
elsif ( $op eq 'delete_confirmed' ) {
|
||||
|
||||
$schema->resultset('VendorEdiAccount')
|
||||
->search( { id => $input->param('id'), } )->delete_all;
|
||||
}
|
||||
|
||||
# we do a default dispaly after deletes and saves
|
||||
# as well as when thats all you want
|
||||
$template->param( display => 1 );
|
||||
my @ediaccounts = $schema->resultset('VendorEdiAccount')->search(
|
||||
{},
|
||||
{
|
||||
join => 'vendor',
|
||||
}
|
||||
);
|
||||
$template->param( ediaccounts => \@ediaccounts );
|
||||
}
|
||||
|
||||
output_html_with_http_headers( $input, $cookie, $template->output );
|
||||
|
||||
sub get_account {
|
||||
my $id = shift;
|
||||
|
||||
my $account = $schema->resultset('VendorEdiAccount')->find($id);
|
||||
if ($account) {
|
||||
return $account;
|
||||
}
|
||||
|
||||
# passing undef will default to add
|
||||
return;
|
||||
}
|
||||
|
||||
sub show_account {
|
||||
my $acct_id = $input->param('id');
|
||||
if ($acct_id) {
|
||||
my $acct = $schema->resultset('VendorEdiAccount')->find($acct_id);
|
||||
if ($acct) {
|
||||
$template->param( account => $acct );
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Copyright 2012, 2014 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 3 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 Koha::Database;
|
||||
|
||||
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 => { acquisition => 'edi_manage' },
|
||||
}
|
||||
);
|
||||
|
||||
my $schema = Koha::Database->new()->schema();
|
||||
my $op = $input->param('op');
|
||||
$op ||= 'display';
|
||||
|
||||
if ( $op eq 'ean_form' ) {
|
||||
show_ean();
|
||||
$template->param( ean_form => 1 );
|
||||
my @branches = $schema->resultset('Branch')->search(
|
||||
undef,
|
||||
{
|
||||
columns => [ 'branchcode', 'branchname' ],
|
||||
order_by => 'branchname',
|
||||
}
|
||||
);
|
||||
$template->param( branches => \@branches );
|
||||
$template->param(
|
||||
code_qualifiers => [
|
||||
{
|
||||
code => '14',
|
||||
description => 'EAN International',
|
||||
},
|
||||
{
|
||||
code => '31B',
|
||||
description => 'US SAN Agency',
|
||||
},
|
||||
{
|
||||
code => '91',
|
||||
description => 'Assigned by supplier',
|
||||
},
|
||||
{
|
||||
code => '92',
|
||||
description => 'Assigned by buyer',
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
}
|
||||
elsif ( $op eq 'delete_confirm' ) {
|
||||
show_ean();
|
||||
$template->param( delete_confirm => 1 );
|
||||
}
|
||||
else {
|
||||
if ( $op eq 'save' ) {
|
||||
my $change = $input->param('oldean');
|
||||
if ($change) {
|
||||
editsubmit();
|
||||
}
|
||||
else {
|
||||
addsubmit();
|
||||
}
|
||||
}
|
||||
elsif ( $op eq 'delete_confirmed' ) {
|
||||
delsubmit();
|
||||
}
|
||||
my @eans = $schema->resultset('EdifactEan')->search(
|
||||
{},
|
||||
{
|
||||
join => 'branch',
|
||||
}
|
||||
);
|
||||
$template->param( display => 1 );
|
||||
$template->param( eans => \@eans );
|
||||
}
|
||||
|
||||
output_html_with_http_headers( $input, $cookie, $template->output );
|
||||
|
||||
sub delsubmit {
|
||||
my $ean = $schema->resultset('EdifactEan')->find(
|
||||
{
|
||||
branchcode => $input->param('branchcode'),
|
||||
ean => $input->param('ean')
|
||||
}
|
||||
);
|
||||
$ean->delete;
|
||||
return;
|
||||
}
|
||||
|
||||
sub addsubmit {
|
||||
|
||||
my $new_ean = $schema->resultset('EdifactEan')->new(
|
||||
{
|
||||
branchcode => $input->param('branchcode'),
|
||||
ean => $input->param('ean'),
|
||||
id_code_qualifier => $input->param('id_code_qualifier'),
|
||||
}
|
||||
);
|
||||
$new_ean->insert();
|
||||
return;
|
||||
}
|
||||
|
||||
sub editsubmit {
|
||||
$schema->resultset('EdifactEan')->search(
|
||||
{
|
||||
branchcode => $input->param('oldbranchcode'),
|
||||
ean => $input->param('oldean'),
|
||||
}
|
||||
)->update_all(
|
||||
{
|
||||
branchcode => $input->param('branchcode'),
|
||||
ean => $input->param('ean'),
|
||||
id_code_qualifier => $input->param('id_code_qualifier'),
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
sub show_ean {
|
||||
my $branchcode = $input->param('branchcode');
|
||||
my $ean = $input->param('ean');
|
||||
if ( $branchcode && $ean ) {
|
||||
my $e = $schema->resultset('EdifactEan')->find(
|
||||
{
|
||||
ean => $ean,
|
||||
branchcode => $branchcode,
|
||||
}
|
||||
);
|
||||
$template->param( ean => $e );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
-- Holds details for vendors supplying goods by EDI
|
||||
CREATE TABLE IF NOT EXISTS vendor_edi_accounts (
|
||||
id int(11) NOT NULL auto_increment,
|
||||
description text NOT NULL,
|
||||
host varchar(40),
|
||||
username varchar(40),
|
||||
password varchar(40),
|
||||
last_activity date,
|
||||
vendor_id int(11) references aqbooksellers( id ),
|
||||
download_directory text,
|
||||
upload_directory text,
|
||||
san varchar(20),
|
||||
id_code_qualifier varchar(3) default '14',
|
||||
transport varchar(6) default 'FTP',
|
||||
quotes_enabled tinyint(1) not null default 0,
|
||||
invoices_enabled tinyint(1) not null default 0,
|
||||
orders_enabled tinyint(1) not null default 0,
|
||||
responses_enabled tinyint(1) not null default 0,
|
||||
auto_orders tinyint(1) not null default 0,
|
||||
shipment_budget integer(11) references aqbudgets( budget_id ),
|
||||
PRIMARY KEY (id),
|
||||
KEY vendorid (vendor_id),
|
||||
KEY shipmentbudget (shipment_budget),
|
||||
CONSTRAINT vfk_vendor_id FOREIGN KEY ( vendor_id ) REFERENCES aqbooksellers ( id ),
|
||||
CONSTRAINT vfk_shipment_budget FOREIGN KEY ( shipment_budget ) REFERENCES aqbudgets ( budget_id )
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Hold the actual edifact messages with links to associated baskets
|
||||
CREATE TABLE IF NOT EXISTS edifact_messages (
|
||||
id int(11) NOT NULL auto_increment,
|
||||
message_type varchar(10) NOT NULL,
|
||||
transfer_date date,
|
||||
vendor_id int(11) references aqbooksellers( id ),
|
||||
edi_acct integer references vendor_edi_accounts( id ),
|
||||
status text,
|
||||
basketno int(11) REFERENCES aqbasket( basketno),
|
||||
raw_msg mediumtext,
|
||||
filename text,
|
||||
deleted boolean not null default 0,
|
||||
PRIMARY KEY (id),
|
||||
KEY vendorid ( vendor_id),
|
||||
KEY ediacct (edi_acct),
|
||||
KEY basketno ( basketno),
|
||||
CONSTRAINT emfk_vendor FOREIGN KEY ( vendor_id ) REFERENCES aqbooksellers ( id ),
|
||||
CONSTRAINT emfk_edi_acct FOREIGN KEY ( edi_acct ) REFERENCES vendor_edi_accounts ( id ),
|
||||
CONSTRAINT emfk_basketno FOREIGN KEY ( basketno ) REFERENCES aqbasket ( basketno )
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- invoices link back to the edifact message it was generated from
|
||||
ALTER TABLE aqinvoices ADD COLUMN message_id INT(11) REFERENCES edifact_messages( id );
|
||||
|
||||
-- clean up link on deletes
|
||||
ALTER TABLE aqinvoices ADD CONSTRAINT edifact_msg_fk FOREIGN KEY ( message_id ) REFERENCES edifact_messages ( id ) ON DELETE SET NULL;
|
||||
|
||||
-- Hold the supplier ids from quotes for ordering
|
||||
-- although this is an EAN-13 article number the standard says 35 characters ???
|
||||
ALTER TABLE aqorders ADD COLUMN line_item_id varchar(35);
|
||||
|
||||
-- The suppliers unique reference usually a quotation line number ('QLI')
|
||||
-- Otherwise Suppliers unique orderline reference ('SLI')
|
||||
ALTER TABLE aqorders ADD COLUMN suppliers_reference_number varchar(35);
|
||||
ALTER TABLE aqorders ADD COLUMN suppliers_reference_qualifier varchar(3);
|
||||
ALTER TABLE aqorders ADD COLUMN suppliers_report text;
|
||||
|
||||
-- hold the EAN/SAN used in ordering
|
||||
CREATE TABLE IF NOT EXISTS edifact_ean (
|
||||
ee_id integer(11) unsigned not null auto_increment primary key,
|
||||
branchcode VARCHAR(10) NOT NULL REFERENCES branches (branchcode),
|
||||
ean varchar(15) NOT NULL,
|
||||
id_code_qualifier VARCHAR(3) NOT NULL DEFAULT '14',
|
||||
CONSTRAINT efk_branchcode FOREIGN KEY ( branchcode ) REFERENCES branches ( branchcode )
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Syspref budget to hold shipping costs
|
||||
INSERT INTO systempreferences (variable, explanation, type) VALUES('EDIInvoicesShippingBudget','The budget code used to allocate shipping charges to when processing EDI Invoice messages', 'free');
|
||||
|
||||
-- Add a permission for managing EDI
|
||||
INSERT INTO permissions (module_bit, code, description) values (11, 'edi_manage', 'Manage EDIFACT transmissions');
|
||||
|
|
@ -3112,6 +3112,10 @@ CREATE TABLE `aqorders` ( -- information related to the basket line items
|
|||
`subscriptionid` int(11) default NULL, -- links this order line to a subscription (subscription.subscriptionid)
|
||||
parent_ordernumber int(11) default NULL, -- ordernumber of parent order line, or same as ordernumber if no parent
|
||||
`orderstatus` varchar(16) default 'new', -- the current status for this line item. Can be 'new', 'ordered', 'partial', 'complete' or 'cancelled'
|
||||
line_item_id varchar(35) default NULL, -- Supplier's article id for Edifact orderline
|
||||
suppliers_reference_number varchar(35) default NULL, -- Suppliers unique edifact quote ref
|
||||
suppliers_reference_qualifier varchar(3) default NULL, -- Type of number above usually 'QLI'
|
||||
`suppliers_report` text COLLATE utf8_unicode_ci, -- reports received from suppliers
|
||||
PRIMARY KEY (`ordernumber`),
|
||||
KEY `basketno` (`basketno`),
|
||||
KEY `biblionumber` (`biblionumber`),
|
||||
|
|
@ -3169,6 +3173,62 @@ CREATE TABLE aqorders_transfers (
|
|||
CONSTRAINT aqorders_transfers_ordernumber_to FOREIGN KEY (ordernumber_to) REFERENCES aqorders (ordernumber) ON DELETE SET NULL ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
--
|
||||
-- Table structure for table vendor_edi_accounts
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS vendor_edi_accounts;
|
||||
CREATE TABLE IF NOT EXISTS vendor_edi_accounts (
|
||||
id int(11) NOT NULL auto_increment,
|
||||
description text NOT NULL,
|
||||
host varchar(40),
|
||||
username varchar(40),
|
||||
password varchar(40),
|
||||
last_activity date,
|
||||
vendor_id int(11) references aqbooksellers( id ),
|
||||
download_directory text,
|
||||
upload_directory text,
|
||||
san varchar(20),
|
||||
id_code_qualifier varchar(3) default '14',
|
||||
transport varchar(6) default 'FTP',
|
||||
quotes_enabled tinyint(1) not null default 0,
|
||||
invoices_enabled tinyint(1) not null default 0,
|
||||
orders_enabled tinyint(1) not null default 0,
|
||||
responses_enabled tinyint(1) NOT NULL DEFAULT '0',
|
||||
auto_orders tinyint(1) NOT NULL DEFAULT '0',
|
||||
shipment_budget integer(11) references aqbudgets( budget_id ),
|
||||
PRIMARY KEY (id),
|
||||
KEY vendorid (vendor_id),
|
||||
KEY shipmentbudget (shipment_budget),
|
||||
CONSTRAINT vfk_vendor_id FOREIGN KEY ( vendor_id ) REFERENCES aqbooksellers ( id ),
|
||||
CONSTRAINT vfk_shipment_budget FOREIGN KEY ( shipment_budget ) REFERENCES aqbudgets ( budget_id )
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
--
|
||||
-- Table structure for table edifact_messages
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS edifact_messages;
|
||||
CREATE TABLE IF NOT EXISTS edifact_messages (
|
||||
id int(11) NOT NULL auto_increment,
|
||||
message_type varchar(10) NOT NULL,
|
||||
transfer_date date,
|
||||
vendor_id int(11) references aqbooksellers( id ),
|
||||
edi_acct integer references vendor_edi_accounts( id ),
|
||||
status text,
|
||||
basketno int(11) references aqbasket( basketno),
|
||||
raw_msg mediumtext,
|
||||
filename text,
|
||||
deleted boolean not null default 0,
|
||||
PRIMARY KEY (id),
|
||||
KEY vendorid ( vendor_id),
|
||||
KEY ediacct (edi_acct),
|
||||
KEY basketno ( basketno),
|
||||
CONSTRAINT emfk_vendor FOREIGN KEY ( vendor_id ) REFERENCES aqbooksellers ( id ),
|
||||
CONSTRAINT emfk_edi_acct FOREIGN KEY ( edi_acct ) REFERENCES vendor_edi_accounts ( id ),
|
||||
CONSTRAINT emfk_basketno FOREIGN KEY ( basketno ) REFERENCES aqbasket ( basketno )
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
--
|
||||
-- Table structure for table aqinvoices
|
||||
--
|
||||
|
|
@ -3183,8 +3243,10 @@ CREATE TABLE aqinvoices (
|
|||
closedate date default NULL, -- invoice close date, NULL means the invoice is open
|
||||
shipmentcost decimal(28,6) default NULL, -- shipment cost
|
||||
shipmentcost_budgetid int(11) default NULL, -- foreign key to aqbudgets, link the shipment cost to a budget
|
||||
message_id int(11) default NULL, -- foreign key to edifact invoice message
|
||||
PRIMARY KEY (invoiceid),
|
||||
CONSTRAINT aqinvoices_fk_aqbooksellerid FOREIGN KEY (booksellerid) REFERENCES aqbooksellers (id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT edifact_msg_fk FOREIGN KEY ( message_id ) REFERENCES edifact_messages ( id ) ON DELETE SET NULL,
|
||||
CONSTRAINT aqinvoices_fk_shipmentcost_budgetid FOREIGN KEY (shipmentcost_budgetid) REFERENCES aqbudgets (budget_id) ON DELETE SET NULL ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
|
|
@ -3646,6 +3708,20 @@ CREATE TABLE audio_alerts (
|
|||
KEY precedence (precedence)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
--
|
||||
-- Table structure for table 'edifact_ean'
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS edifact_ean;
|
||||
CREATE TABLE IF NOT EXISTS edifact_ean (
|
||||
ee_id int(11) NOT NULL AUTO_INCREMENT,
|
||||
branchcode varchar(10) not null references branches (branchcode),
|
||||
ean varchar(15) NOT NULL,
|
||||
id_code_qualifier varchar(3) NOT NULL default '14',
|
||||
PRIMARY KEY (ee_id),
|
||||
CONSTRAINT efk_branchcode FOREIGN KEY ( branchcode ) REFERENCES branches ( branchcode )
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `
|
|||
('DumpTemplateVarsIntranet', '0', NULL , 'If enabled, dump all Template Toolkit variable to a comment in the html source for the staff intranet.', 'YesNo'),
|
||||
('DumpTemplateVarsOpac', '0', NULL , 'If enabled, dump all Template Toolkit variable to a comment in the html source for the opac.', 'YesNo'),
|
||||
('EasyAnalyticalRecords','0','','If on, display in the catalogue screens tools to easily setup analytical record relationships','YesNo'),
|
||||
('EDIInvoicesShippingBudget',NULL,NULL,'The budget code used to allocate shipping charges to when processing EDI Invoice messages','free'),
|
||||
('emailLibrarianWhenHoldIsPlaced','0',NULL,'If ON, emails the librarian whenever a hold is placed','YesNo'),
|
||||
('EnableAdvancedCatalogingEditor','0','','Enable the Rancor advanced cataloging editor','YesNo'),
|
||||
('EnableBorrowerFiles','0',NULL,'If enabled, allows librarians to upload and attach arbitrary files to a borrower record.','YesNo'),
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES
|
|||
(11, 'order_receive', 'Manage orders & basket'),
|
||||
(11, 'budget_add_del', 'Add and delete budgets (but can''t modify budgets)'),
|
||||
(11, 'budget_manage_all', 'Manage all budgets'),
|
||||
(11, 'edi_manage', 'Manage EDIFACT transmissions'),
|
||||
(13, 'edit_news', 'Write news for the OPAC and staff interfaces'),
|
||||
(13, 'label_creator', 'Create printable labels and barcodes from catalog and patron data'),
|
||||
(13, 'edit_calendar', 'Define days when the library is closed'),
|
||||
|
|
|
|||
|
|
@ -9,4 +9,7 @@
|
|||
[% IF ( CAN_user_parameters ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/currency.pl">Currencies</a></li>
|
||||
[% END %]
|
||||
[% IF CAN_user_acquisition_edi_manage %]
|
||||
<li><a href="/cgi-bin/koha/acqui/edifactmsgs.pl">Edifact Messages</a></li>
|
||||
[% END %]
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@
|
|||
<li><a href="/cgi-bin/koha/admin/currency.pl">Currencies and exchange rates</a></li>
|
||||
<li><a href="/cgi-bin/koha/admin/aqbudgetperiods.pl">Budgets</a></li>
|
||||
<li><a href="/cgi-bin/koha/admin/aqbudgets.pl">Funds</a></li>
|
||||
<li><a href="/cgi-bin/koha/admin/edi_accounts.pl">EDI accounts</a></li>
|
||||
<li><a href="/cgi-bin/koha/admin/edi_ean_accounts.pl">EDI eans</a></li>
|
||||
</ul>
|
||||
|
||||
[% IF CAN_user_plugins %]
|
||||
|
|
|
|||
|
|
@ -51,6 +51,13 @@
|
|||
window.open(url, 'TransferOrder','width=600,height=400,toolbar=false,scrollbars=yes');
|
||||
}
|
||||
|
||||
function confirm_ediorder() {
|
||||
var is_confirmed = confirm(_("Are you sure you want to close this basket and generate an Edifact order?"));
|
||||
if (is_confirmed) {
|
||||
window.location = "[% script_name %]?op=edi_confirm&basketno=[% basketno %]";
|
||||
}
|
||||
}
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
[% ELSE %]
|
||||
|
|
@ -157,7 +164,7 @@
|
|||
</div>
|
||||
[% ELSE %]
|
||||
<div class="yui-b">
|
||||
[% UNLESS ( confirm_close ) %]
|
||||
[% IF !confirm_close && !edi_confirm %]
|
||||
[% UNLESS ( selectbasketg ) %]
|
||||
[% UNLESS ( closedate ) %]
|
||||
<div id="toolbar" class="btn-toolbar">
|
||||
|
|
@ -176,6 +183,9 @@
|
|||
</div>
|
||||
[% END %]
|
||||
<div class="btn-group"><a href="[% script_name %]?op=export&basketno=[% basketno %]&booksellerid=[% booksellerid %]" class="btn btn-small" id="exportbutton"><i class="fa fa-download"></i> Export this basket as CSV</a></div>
|
||||
[% IF ediaccount %]
|
||||
<div class="btn-group"><a href="/cgi-bin/koha/acqui/edi_ean.pl?op=ediorder&basketno=[% basketno %]&booksellerid=[% booksellerid %]" class="btn btn-small" id="ediorderbutton"><i class="icon-download"></i>Create edifact order</a></div>
|
||||
[% END %]
|
||||
</div>
|
||||
<!-- Modal for confirm deletion box-->
|
||||
<div class="modal hide" id="deleteBasketModal" tabindex="-1" role="dialog" aria-labelledby="delbasketModalLabel" aria-hidden="true">
|
||||
|
|
@ -392,6 +402,7 @@
|
|||
<th>GST %</th>
|
||||
<th>GST</th>
|
||||
<th>Fund</th>
|
||||
<th>Supplier report</th>
|
||||
[% IF ( active ) %]
|
||||
[% UNLESS ( closedate ) %]
|
||||
<th>Modify</th>
|
||||
|
|
@ -415,6 +426,7 @@
|
|||
<th> </th>
|
||||
<th>[% foot_loo.gstvalue | $Price %]</th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
[% IF ( active ) %]
|
||||
[% UNLESS ( closedate ) %]
|
||||
<th> </th>
|
||||
|
|
@ -436,6 +448,7 @@
|
|||
<th> </th>
|
||||
<th>[% total_gstvalue | $Price %]</th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
[% IF ( active ) %]
|
||||
[% UNLESS ( closedate ) %]
|
||||
<th> </th>
|
||||
|
|
@ -509,6 +522,7 @@
|
|||
<td class="number">[% books_loo.gstrate * 100 | $Price %]</td>
|
||||
<td class="number [% IF books_loo.gstvalue.search(zero_regex) %]error[% END %]">[% books_loo.gstvalue | $Price %]</td>
|
||||
<td>[% books_loo.budget_name %]</td>
|
||||
<td>[% books_loo.suppliers_report %]</td>
|
||||
[% IF ( active ) %]
|
||||
[% UNLESS ( closedate ) %]
|
||||
<td>
|
||||
|
|
@ -670,6 +684,28 @@
|
|||
</form>
|
||||
</div>
|
||||
[% END %]
|
||||
[% IF edi_confirm %]
|
||||
<div id="closebasket_needsconfirmation" class="dialog alert">
|
||||
|
||||
<form action="/cgi-bin/koha/acqui/basket.pl" class="confirm">
|
||||
<h1>Are you sure you want to generate an edifact order and close basket [% basketname|html %]?</h1>
|
||||
[% IF CAN_user_acquisition_group_manage %]
|
||||
<p>
|
||||
<label for="createbasketgroup">Attach this basket to a new basket group with the same name</label>
|
||||
<input type="checkbox" id="createbasketgroup" name="createbasketgroup"/>
|
||||
</p>
|
||||
[% END %]
|
||||
<input type="hidden" id="basketno" value="[% basketno %]" name="basketno" />
|
||||
<input type="hidden" value="ediorder" name="op" />
|
||||
<input type="hidden" name="ean" value="[% ean %]" />
|
||||
<input type="hidden" name="booksellerid" value="[% booksellerid %]" />
|
||||
<input type="hidden" name="confirm" value="1" />
|
||||
<input type="hidden" name="basketgroupname" value="[% basketgroupname %]" />
|
||||
<input type="submit" class="approve" value="Yes, close (Y)" accesskey="y" />
|
||||
<input type="submit" class="deny" value="No, don't close (N)" accesskey="n" onclick="javascript:window.location='/cgi-bin/koha/acqui/basket.pl?basketno=[% basketno %]';return false;" />
|
||||
</form>
|
||||
</div>
|
||||
[% END %]
|
||||
</div>
|
||||
[% END %][%# IF (cannot_manage_basket) %]
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ function submitForm(form) {
|
|||
<div class="btn-group"><a href="[% script_name %]?op=reopen&basketgroupid=[% basketgroupid %]&booksellerid=[% booksellerid %]&mode=singlebg" class="btn btn-small" id="reopenbutton"><i class="fa fa-download"></i> Reopen this basket group</a></div>
|
||||
<div class="btn-group"><a href="[% script_name %]?op=export&basketgroupid=[% basketgroupid %]&booksellerid=[% booksellerid %]" class="btn btn-small" id="exportbutton"><i class="fa fa-download"></i> Export this basket group as CSV</a></div>
|
||||
<div class="btn-group"><a href="[% script_name %]?op=print&basketgroupid=[% basketgroupid %]&booksellerid=[% booksellerid %]" class="btn btn-small" id="printbutton"><i class="fa fa-download"></i> Print this basket group in PDF</a></div>
|
||||
<div class="btn-group"><a href="[% script_name %]?op=ediprint&basketgroupid=[% basketgroupid %]&booksellerid=[% booksellerid %]" class="btn btn-small" id="printbutton"><i class="icon-download"></i>Generate edifact order</a></div>
|
||||
</div>
|
||||
[% ELSE %]
|
||||
<div class="btn-group"><a href="[% script_name %]?op=delete&basketgroupid=[% basketgroupid %]&booksellerid=[% booksellerid %]" class="btn btn-small" id="delbutton"><i class="fa fa-remove"></i> Delete basket group</a></div>
|
||||
|
|
@ -380,6 +381,7 @@ function submitForm(form) {
|
|||
<form action="/cgi-bin/koha/acqui/basketgroup.pl" method="get"><input type="hidden" name="op" value="reopen" /><input type="hidden" name="booksellerid" value="[% basketgroup.booksellerid %]" /><input type="hidden" name="basketgroupid" value="[% basketgroup.id %]" /><input type="submit" value="Reopen" /></form>
|
||||
<form action="/cgi-bin/koha/acqui/basketgroup.pl" method="get"><input type="hidden" name="op" value="print" /><input type="hidden" name="basketgroupid" value="[% basketgroup.id %]" /><input type="submit" value="Export as PDF" /></form>
|
||||
<form action="/cgi-bin/koha/acqui/basketgroup.pl" method="get"><input type="hidden" name="op" value="export" /><input type="hidden" name="basketgroupid" value="[% basketgroup.id %]" /><input type="submit" value="Export as CSV" /></form>
|
||||
<form action="/cgi-bin/koha/acqui/basketgroup.pl" method="get"><input type="hidden" name="op" value="ediprint" /><input type="hidden" name="basketgroupid" value="[% basketgroup.id %]" /><input type="submit" value="Generate edifact order" /></form>
|
||||
</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
[% INCLUDE 'doc-head-open.inc' %]
|
||||
<title>Koha › Acquisitions › Basket ([% basketno %])</title>
|
||||
[% INCLUDE 'doc-head-close.inc' %]
|
||||
|
||||
</head>
|
||||
<body>
|
||||
[% INCLUDE 'header.inc' %]
|
||||
[% INCLUDE 'acquisitions-search.inc' %]
|
||||
|
||||
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> › <a href="/cgi-bin/koha/acqui/acqui-home.pl">Acquisitions</a> › Basket ([% basketno %])</div>
|
||||
|
||||
<div id="doc3" class="yui-t2">
|
||||
|
||||
<div id="bd">
|
||||
<div id="yui-main">
|
||||
<div class="yui-b">
|
||||
|
||||
<h2>Identify the branch account submitting the EDI order</h2>
|
||||
<br />
|
||||
<form action="/cgi-bin/koha/acqui/basket.pl" method="get">
|
||||
<p>Select ordering branch account: </p>
|
||||
<select id="ean" name="ean">
|
||||
[% FOREACH eanacct IN eans %]
|
||||
<option value="[% eanacct.ean %]">[% eanacct.branch.branchname %] ([% eanacct.ean %])</option>
|
||||
[% END %]
|
||||
</select>
|
||||
<br />
|
||||
<input type="hidden" id="basketno" value="[% basketno %]" name="basketno" />
|
||||
<input type="hidden" value="ediorder" name="op" />
|
||||
<input type="submit" value="Send EDI order" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="yui-b">
|
||||
[% INCLUDE 'acquisitions-menu.inc' %]
|
||||
</div>
|
||||
</div>
|
||||
[% INCLUDE 'intranet-bottom.inc' %]
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
[% INCLUDE 'doc-head-open.inc' %]
|
||||
<title>Koha › Acquisitions</title>
|
||||
<link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" />
|
||||
[% INCLUDE 'doc-head-close.inc' %]
|
||||
[% INCLUDE 'datatables.inc' %]
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#edi_msgs').dataTable($.extend(true, {}, dataTablesDefaults, {
|
||||
'aaSorting': [[1, "desc" ]],
|
||||
'sPaginationType': "four_button"
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body id="acq_edifactmsgs" class="acq">
|
||||
[% INCLUDE 'header.inc' %]
|
||||
[% INCLUDE 'acquisitions-search.inc' %]
|
||||
<div id="breadcrumbs">
|
||||
<a href="/cgi-bin/koha/mainpage.pl">Home</a>
|
||||
› <a href="/cgi-bin/koha/acqui/acqui-home.pl">Acquisitions</a>
|
||||
› <a href="/cgi-bin/koha/acqui/edifactmsgs.pl">Edifact Messages</a>
|
||||
</div>
|
||||
|
||||
<div id="doc3" class="yui-t2">
|
||||
|
||||
<div id="bd">
|
||||
<div id="yui-main">
|
||||
<div class="yui-b">
|
||||
|
||||
|
||||
<h1>Edifact Messages</h1>
|
||||
<div id="acqui_edifactmsgs">
|
||||
|
||||
|
||||
<table id="edi_msgs">
|
||||
<thead>
|
||||
<th>Type</th>
|
||||
<th>Transferred</th>
|
||||
<th>Status</th>
|
||||
<th>Vendor</th>
|
||||
<th>Details</th>
|
||||
<th>Filename</th>
|
||||
<th> </th>
|
||||
<th>Action</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
[% FOREACH msg IN messages %]
|
||||
<tr>
|
||||
<td>[% msg.message_type %]</td>
|
||||
<td>[% msg.transfer_date %]</td>
|
||||
<td>[% msg.status %]</td>
|
||||
<td>
|
||||
<a href="/cgi-bin/koha/acqui/supplier.pl?booksellerid=[% msg.vendor_id %]"</a>
|
||||
[% msg.vendor.name %]
|
||||
</td>
|
||||
<td>
|
||||
[% IF msg.message_type == 'QUOTE' || msg.message_type == 'ORDERS' %]
|
||||
[% IF msg.basketno %]
|
||||
<a href="/cgi-bin/koha/acqui/basket.pl?basketno=[% msg.basketno.basketno %]">
|
||||
Basket: [% msg.basketno.basketno %]
|
||||
</a>
|
||||
[% END %]
|
||||
[% ELSE %]
|
||||
<!-- Assuming invoices -->
|
||||
<a href="/cgi-bin/koha/acqui/invoices.pl?message_id=[% msg.id %]">
|
||||
Invoices
|
||||
</a>
|
||||
[% END %]
|
||||
</td>
|
||||
</td>
|
||||
<td>[% msg.filename %]</td>
|
||||
<td><a class="popup" target="_blank" title="View Message" href="/cgi-bin/koha/acqui/edimsg.pl?id=[% msg.id %]"</a>View Message</td>
|
||||
<td>
|
||||
<a href="/cgi-bin/koha/acqui/edifactmsgs.pl?op=delete&message_id=[% msg.id %]">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="yui-b">
|
||||
[% INCLUDE 'acquisitions-menu.inc' %]
|
||||
</div>
|
||||
</div>
|
||||
[% INCLUDE 'intranet-bottom.inc' %]
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
[% INCLUDE 'doc-head-open.inc' %]
|
||||
<title>Koha › Acquisitions › Edifact Message Display</title>
|
||||
[% INCLUDE 'doc-head-close.inc' %]
|
||||
</head>
|
||||
<body id="acq_edifactmsgs" class="acq">
|
||||
[% INCLUDE 'header.inc' %]
|
||||
[% INCLUDE 'acquisitions-search.inc' %]
|
||||
<div id="breadcrumbs">
|
||||
<a href="/cgi-bin/koha/mainpage.pl">Home</a>
|
||||
› <a href="/cgi-bin/koha/acqui/acqui-home.pl">Acquisitions</a>
|
||||
› <a href="/cgi-bin/koha/acqui/edifactmsgs.pl">Edifact Messages</a>
|
||||
</div>
|
||||
|
||||
<div id="doc3" class="yui-t2">
|
||||
|
||||
[% IF no_message %]
|
||||
<div class"dialog message">The requested message cannot be displayed</div>
|
||||
[% ELSE %]
|
||||
<div id="bd">
|
||||
<div id="yui-main">
|
||||
<div class="yui-b">
|
||||
<ul>
|
||||
[% FOREACH seg IN segments %]
|
||||
<li>[% seg | html %]</li>
|
||||
[% END %]
|
||||
</ul>
|
||||
[% END %]
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="yui-b">
|
||||
[% INCLUDE 'acquisitions-menu.inc' %]
|
||||
</div>
|
||||
</div>
|
||||
[% INCLUDE 'intranet-bottom.inc' %]
|
||||
|
|
@ -97,6 +97,11 @@
|
|||
|
||||
<dt><a href="/cgi-bin/koha/admin/aqbudgets.pl">Funds</a></dt>
|
||||
<dd>Define funds within your budgets</dd>
|
||||
|
||||
<dt><a href="/cgi-bin/koha/admin/edi_accounts.pl">EDI Accounts</a></dt>
|
||||
<dd>Manage vendor EDI accounts for import/export</dd>
|
||||
<dt><a href="/cgi-bin/koha/admin/edi_ean_accounts.pl">EDI EANs</a></dt>
|
||||
<dd>Manage Branch EDI EANs</dd>
|
||||
</dl>
|
||||
|
||||
<h3>Additional parameters</h3>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,299 @@
|
|||
[% INCLUDE 'doc-head-open.inc' %]
|
||||
<title>Koha › Administration › EDI accounts
|
||||
[% IF acct_form %]
|
||||
[% IF account %]
|
||||
’ Modify account
|
||||
[% ELSE %]
|
||||
’ Add new account
|
||||
[% END %]
|
||||
[% END %]
|
||||
[% IF delete_confirm %]
|
||||
’ Confirm deletion of account
|
||||
[% END %]
|
||||
</title>
|
||||
[% INCLUDE 'doc-head-close.inc' %]
|
||||
</head>
|
||||
<body id="admin_edi_acct" class="admin">
|
||||
[% INCLUDE 'header.inc' %]
|
||||
[% INCLUDE 'cat-search.inc' %]
|
||||
|
||||
<div id="breadcrumbs">
|
||||
<a href="/cgi-bin/koha/mainpage.pl">Home</a>
|
||||
› <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
|
||||
› <a href="/cgi-bin/koha/admin/edi_accounts.pl">EDI accounts</a>
|
||||
[% IF acct_form %]
|
||||
[% IF account %]
|
||||
› Modify account
|
||||
[% ELSE %]
|
||||
› Add new account
|
||||
[% END %]
|
||||
[% ELSIF delete_confirm %]
|
||||
› Confirm deletion of account
|
||||
[% ELSE %]
|
||||
› Accounts
|
||||
[% END %]
|
||||
</div>
|
||||
|
||||
<div id="doc3" class="yui-t2">
|
||||
|
||||
<div id="bd">
|
||||
<div id="yui-main">
|
||||
<div class="yui-b">
|
||||
[% IF display %]
|
||||
<div id="toolbar" class="btn-toolbar">
|
||||
<a class="btn btn-small" id="newediacct" href="/cgi-bin/koha/admin/edi_accounts.pl?op=acct_form">
|
||||
<i class="icon-plus"></i>
|
||||
New account
|
||||
</a>
|
||||
</div>
|
||||
[% END %]
|
||||
|
||||
[% IF acct_form %]
|
||||
<form action="/cgi-bin/koha/admin/edi_accounts.pl" name="Actform" method="post">
|
||||
<input type="hidden" name="op" value="save" />
|
||||
[% IF account %]
|
||||
<input type="hidden" name="id" value="[% account.id %]" />
|
||||
[% END %]
|
||||
<fieldset class="rows">
|
||||
<legend>
|
||||
[% IF account %]
|
||||
Modify account
|
||||
[% ELSE %]
|
||||
New account
|
||||
[% END %]
|
||||
</legend>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
<label for="vendor_id">Vendor: </label>
|
||||
<select name="vendor_id" id="vendor_id">
|
||||
[% FOREACH vendor IN vendors %]
|
||||
[% IF account.vendor_id == vendor.id %]
|
||||
<option value="[% vendor.id %]" selected="selected">[% vendor.name %]</option>
|
||||
[% ELSE %]
|
||||
<option value="[% vendor.id %]">[% vendor.name %]</option>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<label for="description">Description: </label>
|
||||
<input type="text" name="description" id="description" size="20" maxlength="90" value="[% account.description %]" />
|
||||
</li>
|
||||
<li>
|
||||
[% transport_types = [
|
||||
'FTP', 'SFTP', 'FILE'
|
||||
]
|
||||
%]
|
||||
<label for="transport">Transport: </label>
|
||||
<select name="transport" title="valid types of transport are FTP and SFTP"
|
||||
id="transport">
|
||||
[% FOREACH transport_type IN transport_types %]
|
||||
[% IF transport_type == account.transport %]
|
||||
<option value="[% transport_type %]" selected="selected">[% transport_type %]</option>
|
||||
[% ELSE %]
|
||||
<option value="[% transport_type %]">[% transport_type %]</option>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<label for="host">Remote host: </label>
|
||||
<input type="text" name="host" id="host" size="20" maxlength="90" value="[% account.host %]" />
|
||||
</li>
|
||||
<li>
|
||||
<label for="username">Username: </label>
|
||||
<input type="text" name="username" id="username" size="20" maxlength="90" value="[% account.username %]" />
|
||||
</li>
|
||||
<li>
|
||||
<label for="password">Password: </label>
|
||||
<input type="text" name="password" id="password" size="20" maxlength="90" value="[% account.password %]" />
|
||||
</li>
|
||||
<li>
|
||||
<label for="download_directory">Download directory: </label>
|
||||
<input type="text" name="download_directory" id="download_directory" size="20" maxlength="90"
|
||||
title="The download directory specifies the directory on the ftpsite from which we download quotes and invoices"
|
||||
value="[% account.download_directory %]" />
|
||||
</li>
|
||||
<li>
|
||||
<label for="upload_directory">Upload directory: </label>
|
||||
<input type="text" name="upload_directory" id="upload_directory" size="20" maxlength="90"
|
||||
title="The upload directory specifies the directory on the ftp site to which we upload orders"
|
||||
value="[% account.upload_directory %]" />
|
||||
</li>
|
||||
<li>
|
||||
<label for="id_code_qualifier">Qualifier:</label>
|
||||
<select name="id_code_qualifier" id="id_code_qualifier">
|
||||
[% FOREACH qualifier IN code_qualifiers %]
|
||||
[% IF qualifier.code == account.id_code_qualifier %]
|
||||
<option value="[% qualifier.code %]" selected="selected">
|
||||
[% qualifier.description %]
|
||||
</option>
|
||||
[% ELSE %]
|
||||
<option value="[% qualifier.code %]">
|
||||
[% qualifier.description %]
|
||||
</option>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<label for="san">SAN: </label>
|
||||
<input type="text" name="san" id="san" size="20" maxlength="90" value="[% account.san %]" />
|
||||
</li>
|
||||
<li>
|
||||
<label for="quotes_enabled">Quotes enabled: </label>
|
||||
[% IF account.quotes_enabled %]
|
||||
<input type="checkbox" name="quotes_enabled" id="quotes_enabled" value="[% account.quotes_enabled %]" checked />
|
||||
[% ELSE %]
|
||||
<input type="checkbox" name="quotes_enabled" id="quotes_enabled" value="[% account.quotes_enabled %]" />
|
||||
[% END %]
|
||||
</li>
|
||||
<li>
|
||||
<label for="orders_enabled">Orders enabled: </label>
|
||||
[% IF account.orders_enabled %]
|
||||
<input type="checkbox" name="orders_enabled" id="orders_enabled" value="[% account.orders_enabled %]" checked />
|
||||
[% ELSE %]
|
||||
<input type="checkbox" name="orders_enabled" id="orders_enabled" value="[% account.orders_enabled %]" />
|
||||
[% END %]
|
||||
</li>
|
||||
<li>
|
||||
<label for="invoices_enabled">Invoices enabled: </label>
|
||||
[% IF account.invoices_enabled %]
|
||||
<input type="checkbox" name="invoices_enabled" id="invoices_enabled" value="[% account.invoices_enabled %]" checked />
|
||||
[% ELSE %]
|
||||
<input type="checkbox" name="invoices_enabled" id="invoices_enabled" value="[% account.invoices_enabled %]" />
|
||||
[% END %]
|
||||
</li>
|
||||
<li>
|
||||
<label for="responses_enabled">Responses enabled: </label>
|
||||
[% IF account.responses_enabled %]
|
||||
<input type="checkbox" name="responses_enabled" id="responses_enabled" value="[% account.responses_enabled %]" checked />
|
||||
[% ELSE %]
|
||||
<input type="checkbox" name="responses_enabled" id="responses_enabled" value="[% account.responses_enabled %]" />
|
||||
[% END %]
|
||||
</li>
|
||||
<li>
|
||||
<label for="auto_orders">Automatic ordring (Quotes generate orders without staff intervention): </label>
|
||||
[% IF account.auto_orders %]
|
||||
<input type="checkbox" name="auto_orders" id="auto_orders" value="[% account.auto_orders %]" checked />
|
||||
[% ELSE %]
|
||||
<input type="checkbox" name="auto_orders" id="auto_orders" value="[% account.auto_orders %]" />
|
||||
[% END %]
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="action">
|
||||
<input type="submit" value="Submit" />
|
||||
<a href="/cgi-bin/koha/admin/edi_accounts.pl" class="cancel">Cancel</a>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
[% END %]
|
||||
[% IF delete_confirm %]
|
||||
<div class="dialog alert">
|
||||
<h3>Delete this account?</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Vendor</th>
|
||||
<td>[% account.vendor %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<td>[% account.description %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>SAN</th>
|
||||
<td>[% account.san %]</td>
|
||||
</tr>
|
||||
</table>
|
||||
<form action="/cgi-bin/koha/admin/edi_accounts.pl" method="post">
|
||||
<table>
|
||||
</table>
|
||||
<input type="hidden" name="op" value="delete_confirmed" />
|
||||
<input type="hidden" name="id" value="[% account.id %]" />
|
||||
<input type="submit" class="approve" value="Yes, Delete" />
|
||||
</form>
|
||||
<form action="/cgi-bin/koha/admin/edi_accounts.pl" method="get">
|
||||
<input type="submit" class="deny" value="No, do not Delete" />
|
||||
</form>
|
||||
[% END %]
|
||||
[% IF display %]
|
||||
<h2>Vendor EDI accounts</h2>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Vendor</th>
|
||||
<th>Description</th>
|
||||
<th>Transport</th>
|
||||
<th>Remote host</th>
|
||||
<th>Username</th>
|
||||
<th>Password</th>
|
||||
<th>Download Directory</th>
|
||||
<th>Upload Directory</th>
|
||||
<th>id_code_type</th>
|
||||
<th>id_code</th>
|
||||
<th>Quotes</th>
|
||||
<th>Orders</th>
|
||||
<th>Invoices</th>
|
||||
<th>Responses</th>
|
||||
<th>Auto ordering</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
[% FOREACH account IN ediaccounts %]
|
||||
[% IF loop.even %]<tr>
|
||||
[% ELSE %]<tr class="highlight">
|
||||
[% END %]
|
||||
<td>[% account.id %]</td>
|
||||
<td><a href="/cgi-bin/koha/acqui/supplier.pl?supplierid=[% account.vendor_id %]">[% account.vendor.name %]</a></td>
|
||||
<td>[% account.description %]</td>
|
||||
<td>[% account.transport %]</td>
|
||||
<td>[% account.host %]</td>
|
||||
<td>[% account.username %]</td>
|
||||
<td>[% IF account.password %]xxxxx[% END %]</td>
|
||||
<td>[% account.download_directory %]</td>
|
||||
<td>[% account.upload_directory %]</td>
|
||||
<td>[% account.id_code_qualifier %]</td>
|
||||
<td>[% account.san %]</td>
|
||||
[% IF account.quotes_enabled %]
|
||||
<td>Y</td>
|
||||
[% ELSE %]
|
||||
<td>N</td>
|
||||
[% END %]
|
||||
[% IF account.orders_enabled %]
|
||||
<td>Y</td>
|
||||
[% ELSE %]
|
||||
<td>N</td>
|
||||
[% END %]
|
||||
[% IF account.invoices_enabled %]
|
||||
<td>Y</td>
|
||||
[% ELSE %]
|
||||
<td>N</td>
|
||||
[% END %]
|
||||
[% IF account.responses_enabled %]
|
||||
<td>Y</td>
|
||||
[% ELSE %]
|
||||
<td>N</td>
|
||||
[% END %]
|
||||
[% IF account.auto_orders %]
|
||||
<td>Y</td>
|
||||
[% ELSE %]
|
||||
<td>N</td>
|
||||
[% END %]
|
||||
<td align="center">
|
||||
<a href="/cgi-bin/koha/admin/edi_accounts.pl?op=acct_form&id=[% account.id %]">Edit</a> | <a href="/cgi-bin/koha/admin/edi_accounts.pl?op=delete_confirm&id=[% account.id %]">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
</table>
|
||||
[% END %]
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="yui-b">
|
||||
[% INCLUDE 'admin-menu.inc' %]
|
||||
</div>
|
||||
</div>
|
||||
[% INCLUDE 'intranet-bottom.inc' %]
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
[% INCLUDE 'doc-head-open.inc' %]
|
||||
<title>Koha › Administration › EDI EANs</title>
|
||||
[% IF ean_form %]
|
||||
[% IF ean %]
|
||||
’ Modify branch EAN
|
||||
[% ELSE %]
|
||||
’ Add new branch EAN
|
||||
[% END %]
|
||||
[% END %]
|
||||
[% IF delete_confirm %]
|
||||
’ Confirm deletion of EAN
|
||||
[% END %]
|
||||
</title>
|
||||
[% INCLUDE 'doc-head-close.inc' %]
|
||||
</head>
|
||||
<body id="admin_edi_ean" class="admin">
|
||||
[% INCLUDE 'header.inc' %]
|
||||
[% INCLUDE 'cat-search.inc' %]
|
||||
|
||||
<div id="breadcrumbs">
|
||||
<a href="/cgi-bin/koha/mainpage.pl">Home</a>
|
||||
› <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
|
||||
› <a href="/cgi-bin/koha/admin/edi_ean_accounts.pl">EDI EANs</a>
|
||||
[% IF ean_form %]
|
||||
[% IF ean %]
|
||||
› Modify branch EAN
|
||||
[% ELSE %]
|
||||
› Add new branch EAN
|
||||
[% END %]
|
||||
[% ELSIF delete_confirm %]
|
||||
› Confirm deletion of Ean
|
||||
[% ELSE %]
|
||||
› Branch EANs
|
||||
[% END %]
|
||||
</div>
|
||||
|
||||
<div id="doc3" class="yui-t2">
|
||||
|
||||
<div id="bd">
|
||||
<div id="yui-main">
|
||||
<div class="yui-b">
|
||||
[% IF display %]
|
||||
<div id="toolbar" class="btn-toolbar">
|
||||
<a class="btn btn-small" id="newediean" href="/cgi-bin/koha/admin/edi_ean_accounts.pl?op=ean_form">
|
||||
<i class="icon-plus"></i>
|
||||
New EAN
|
||||
</a>
|
||||
</div>
|
||||
[% END %]
|
||||
|
||||
[% IF ean_form %]
|
||||
<form action="/cgi-bin/koha/admin/edi_ean_accounts.pl" name="Eanform" method="post">
|
||||
<input type="hidden" name="op" value="save" />
|
||||
[% IF ean %]
|
||||
<input type="hidden" name="oldbranchcode" value="[% branchcode %]" />
|
||||
<input type="hidden" name="oldean" value="[% ean %]" />
|
||||
[% END %]
|
||||
<fieldset class="rows">
|
||||
<legend>
|
||||
[% IF ean %]
|
||||
Modify EAN
|
||||
[% ELSE %]
|
||||
New EAN
|
||||
[% END %]
|
||||
</legend>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
<label for="branchcode">Branchcode: </label>
|
||||
<select name="branchcode" id="branchcode">
|
||||
[% FOREACH branch IN branches %]
|
||||
[% IF branch.branchcode == ean.branch.branchcode %]
|
||||
<option value="[% branch.branchcode %]" selected="selected">[% branch.branchname %]</option>
|
||||
[% ELSE %]
|
||||
<option value="[% branch.branchcode %]">[% branch.branchname %]</option>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<label for="ean">EAN: </label>
|
||||
<input type="text" name="ean" id="ean`" size="20" maxlength="90" value="[% ean.ean %]" />
|
||||
</li>
|
||||
<li>
|
||||
<label for="id_code_qualifier">
|
||||
<select name="id_code_qualifier" id="id_code_qualifier">
|
||||
[% FOREACH qualifier IN code_qualifiers %]
|
||||
[% IF qualifier.code == ean.id_code_qualifier %]
|
||||
<option value="[% qualifier.code %]" selected="selected">
|
||||
[% qualifier.description %]
|
||||
</option>
|
||||
[% ELSE %]
|
||||
<option value="[% qualifier.code %]">
|
||||
[% qualifier.description %]
|
||||
</option>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</li>
|
||||
|
||||
</ol>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="action">
|
||||
<input type="submit" value="Submit"/>
|
||||
<a href="/cgi-bin/koha/admin/edi_ean_accounts.pl" class="cancel">Cancel</a>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
[% END %]
|
||||
[% IF delete_confirm %]
|
||||
<div class="dialog alert">
|
||||
<h3>Delete EAN [% ean.ean %] for branch [% ean.branch.branchname %]?</h3>
|
||||
<form action="/cgi-bin/koha/admin/edi_ean_accounts.pl" method="post">
|
||||
<input type="hidden" name="op" value="delete_confirmed" />
|
||||
<input type="hidden" name="branchcode" value="[% ean.branch.branchcode %]" />
|
||||
<input type="hidden" name="ean" value="[% ean.ean %]" />
|
||||
<input type="submit" class="approve" value="Yes, Delete" />
|
||||
</form>
|
||||
<form action="/cgi-bin/koha/admin/edi_ean_accounts.pl" method="get">
|
||||
<input type="submit" class="deny" value="No, do not Delete" />
|
||||
</form>
|
||||
</div>
|
||||
[% END %]
|
||||
[% IF display %]
|
||||
<h2>Branch EANs</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Branch</th>
|
||||
<th>EAN</th>
|
||||
<th>Code Type</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
[% FOREACH ean IN eans %]
|
||||
[% IF loop.even %]<tr>
|
||||
[% ELSE %]<tr class="highlight">
|
||||
[% END %]
|
||||
<td>[% ean.branch.branchname %]</td>
|
||||
<td>[% ean.ean %]</td>
|
||||
<td>[% ean.id_code_qualifier %]</td>
|
||||
<td align="center">
|
||||
<a href="/cgi-bin/koha/admin/edi_ean_accounts.pl?op=ean_form&branchcode=[% ean.branch.branchcode %]&ean=[% ean.ean %]">Edit</a> | <a href="/cgi-bin/koha/admin/edi_ean_accounts.pl?op=delete_confirm&branchcode=[% ean.branch.branchcode %]&ean=[% ean.ean %]">Delete</a></td>
|
||||
</tr>
|
||||
[% END %]
|
||||
</table>
|
||||
[% END %]
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="yui-b">
|
||||
[% INCLUDE 'admin-menu.inc' %]
|
||||
</div>
|
||||
</div>
|
||||
[% INCLUDE 'intranet-bottom.inc' %]
|
||||
|
|
@ -104,6 +104,11 @@
|
|||
<dd>Use tool plugins</dd>
|
||||
[% END %]
|
||||
|
||||
[% IF CAN_user_acquisition_edi_manage %]
|
||||
<dt><a href="/cgi-bin/koha/tools/edi.pl">EDIfact messages</a></dt>
|
||||
<dd>Manage EDIfact transmissions</dd>
|
||||
[% END %]
|
||||
|
||||
</dl>
|
||||
</div>
|
||||
<div class="yui-u">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
#!/usr/bin/perl
|
||||
#
|
||||
# Copyright 2013,2014,2015 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 3 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 warnings;
|
||||
use strict;
|
||||
use utf8;
|
||||
|
||||
# Handles all the edi processing for a site
|
||||
# loops through the vendor_edifact records and uploads and downloads
|
||||
# edifact files if the appropriate type is enabled
|
||||
# downloaded quotes, invoices and responses are processed here
|
||||
# if orders are enabled and present they are generated and sent
|
||||
# can be run as frequently as required
|
||||
# log messages are appended to logdir/editrace.log
|
||||
|
||||
use C4::Context;
|
||||
use Log::Log4perl qw(:easy);
|
||||
use Koha::Database;
|
||||
use Koha::EDI qw( process_quote process_invoice process_ordrsp);
|
||||
use Koha::Edifact::Transport;
|
||||
use Fcntl qw( :DEFAULT :flock :seek );
|
||||
|
||||
my $logdir = C4::Context->logdir;
|
||||
|
||||
# logging set to trace as this may be what you
|
||||
# want on implementation
|
||||
Log::Log4perl->easy_init(
|
||||
{
|
||||
level => $TRACE,
|
||||
file => ">>$logdir/editrace.log",
|
||||
}
|
||||
);
|
||||
|
||||
# we dont have a lock dir in context so use the logdir
|
||||
my $pidfile = "$logdir/edicron.pid";
|
||||
|
||||
my $pid_handle = check_pidfile();
|
||||
|
||||
my $schema = Koha::Database->new()->schema();
|
||||
|
||||
my @edi_accts = $schema->resultset('VendorEdiAccount')->all();
|
||||
|
||||
my $logger = Log::Log4perl->get_logger();
|
||||
|
||||
for my $acct (@edi_accts) {
|
||||
if ( $acct->quotes_enabled ) {
|
||||
my $downloader = Koha::Edifact::Transport->new( $acct->id );
|
||||
$downloader->download_messages('QUOTE');
|
||||
|
||||
}
|
||||
|
||||
if ( $acct->invoices_enabled ) {
|
||||
my $downloader = Koha::Edifact::Transport->new( $acct->id );
|
||||
$downloader->download_messages('INVOICE');
|
||||
|
||||
}
|
||||
if ( $acct->orders_enabled ) {
|
||||
|
||||
# select pending messages
|
||||
my @pending_orders = $schema->resultset('EdifactMessage')->search(
|
||||
{
|
||||
message_type => 'ORDERS',
|
||||
vendor_id => $acct->vendor_id,
|
||||
status => 'Pending',
|
||||
}
|
||||
);
|
||||
my $uploader = Koha::Edifact::Transport->new( $acct->id );
|
||||
$uploader->upload_messages(@pending_orders);
|
||||
}
|
||||
if ( $acct->responses_enabled ) {
|
||||
my $downloader = Koha::Edifact::Transport->new( $acct->id );
|
||||
$downloader->download_messages('ORDRSP');
|
||||
}
|
||||
}
|
||||
|
||||
# process any downloaded quotes
|
||||
|
||||
my @downloaded_quotes = $schema->resultset('EdifactMessage')->search(
|
||||
{
|
||||
message_type => 'QUOTE',
|
||||
status => 'new',
|
||||
}
|
||||
)->all;
|
||||
|
||||
foreach my $quote_file (@downloaded_quotes) {
|
||||
my $filename = $quote_file->filename;
|
||||
$logger->trace("Processing quote $filename");
|
||||
process_quote($quote_file);
|
||||
}
|
||||
|
||||
# process any downloaded invoices
|
||||
|
||||
my @downloaded_invoices = $schema->resultset('EdifactMessage')->search(
|
||||
{
|
||||
message_type => 'INVOICE',
|
||||
status => 'new',
|
||||
}
|
||||
)->all;
|
||||
|
||||
foreach my $invoice (@downloaded_invoices) {
|
||||
my $filename = $invoice->filename();
|
||||
$logger->trace("Processing invoice $filename");
|
||||
process_invoice($invoice);
|
||||
}
|
||||
|
||||
my @downloaded_responses = $schema->resultset('EdifactMessage')->search(
|
||||
{
|
||||
message_type => 'ORDRSP',
|
||||
status => 'new',
|
||||
}
|
||||
)->all;
|
||||
|
||||
foreach my $response (@downloaded_responses) {
|
||||
my $filename = $response->filename();
|
||||
$logger->trace("Processing order response $filename");
|
||||
process_ordrsp($response);
|
||||
}
|
||||
|
||||
if ( close $pid_handle ) {
|
||||
unlink $pidfile;
|
||||
exit 0;
|
||||
}
|
||||
else {
|
||||
$logger->error("Error on pidfile close: $!");
|
||||
exit 1;
|
||||
}
|
||||
|
||||
sub check_pidfile {
|
||||
|
||||
# sysopen my $fh, $pidfile, O_EXCL | O_RDWR or log_exit "$0 already running"
|
||||
sysopen my $fh, $pidfile, O_RDWR | O_CREAT
|
||||
or log_exit("$0: open $pidfile: $!");
|
||||
flock $fh => LOCK_EX or log_exit("$0: flock $pidfile: $!");
|
||||
|
||||
sysseek $fh, 0, SEEK_SET or log_exit("$0: sysseek $pidfile: $!");
|
||||
truncate $fh, 0 or log_exit("$0: truncate $pidfile: $!");
|
||||
print $fh "$$\n" or log_exit("$0: print $pidfile: $!");
|
||||
|
||||
return $fh;
|
||||
}
|
||||
|
||||
sub log_exit {
|
||||
my $error = shift;
|
||||
$logger->error($error);
|
||||
|
||||
exit 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use C4::Context;
|
||||
|
||||
# this script will remove those older than 5 days
|
||||
my $tmpdir = '/tmp';
|
||||
#
|
||||
opendir( my $dh, $tmpdir) || die "Cannot open $tmpdir : $!";
|
||||
|
||||
my @files_in_tmp = grep { /\.CE[IQ]$/ && -f "$tmpdir/$_" } readdir($dh);
|
||||
closedir $dh;
|
||||
|
||||
|
||||
my $dbh = C4::Context->dbh;
|
||||
|
||||
my $query =<<'ENDSQL';
|
||||
select filename from edifact_messages
|
||||
where message_type IN ('QUOTE','INVOICE')
|
||||
and datediff( CURDATE(), transfer_date ) > 5
|
||||
ENDSQL
|
||||
|
||||
my $ingested;
|
||||
|
||||
@{$ingested} = $dbh->selectcol_arrayref($query);
|
||||
|
||||
my %ingested_hash = map { $_ => 1 } @{$ingested};
|
||||
|
||||
my @delete_list;
|
||||
|
||||
foreach (@files_in_tmp) {
|
||||
if ( exists $ingested_hash{$_} ) {
|
||||
push @delete_list, $_;
|
||||
}
|
||||
}
|
||||
|
||||
if ( @delete_list ) {
|
||||
chdir $tmpdir;
|
||||
unlink @delete_list;
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use FindBin qw( $Bin );
|
||||
|
||||
use Test::More tests => 19;
|
||||
|
||||
BEGIN { use_ok('Koha::Edifact') }
|
||||
|
||||
my $invoice_file = "$Bin/edi_testfiles/BLSINV337023.CEI";
|
||||
|
||||
my $invoice = Koha::Edifact->new( { filename => $invoice_file, } );
|
||||
|
||||
isa_ok( $invoice, 'Koha::Edifact' );
|
||||
my $x = $invoice->interchange_header('sender');
|
||||
my $control_reference = '337023';
|
||||
is( $x, '5013546025078', "sender returned" );
|
||||
|
||||
$x = $invoice->interchange_header('recipient');
|
||||
is( $x, '5013546121974', "recipient returned" );
|
||||
$x = $invoice->interchange_header('datetime');
|
||||
is( $x->[0], '140729', "datetime returned" );
|
||||
$x = $invoice->interchange_header('interchange_control_reference');
|
||||
is( $x, $control_reference, "interchange_control_reference returned" );
|
||||
|
||||
$x = $invoice->interchange_header('application_reference');
|
||||
is( $x, 'INVOIC', "application_reference returned" );
|
||||
$x = $invoice->interchange_trailer('interchange_control_count');
|
||||
is( $x, 6, "interchange_control_count returned" );
|
||||
|
||||
my $messages = $invoice->message_array();
|
||||
|
||||
# check inv number from BGM
|
||||
|
||||
my $msg_count = @{$messages};
|
||||
is( $msg_count, 6, 'correct message count returned' );
|
||||
|
||||
is( $messages->[0]->message_type, 'INVOIC', 'Message shows correct type' );
|
||||
|
||||
my $expected_date = '20140729';
|
||||
is( $messages->[0]->message_date,
|
||||
$expected_date, 'Message date correctly returned' );
|
||||
is( $messages->[0]->tax_point_date,
|
||||
$expected_date, 'Tax point date correctly returned' );
|
||||
|
||||
my $expected_invoicenumber = '01975490';
|
||||
|
||||
my $invoicenumber = $messages->[1]->docmsg_number();
|
||||
|
||||
is( $messages->[0]->buyer_ean, '5013546121974', 'Buyer ean correct' );
|
||||
is( $messages->[0]->supplier_ean, '5013546025078', 'Supplier ean correct' );
|
||||
|
||||
is( $invoicenumber, $expected_invoicenumber,
|
||||
'correct invoicenumber extracted' );
|
||||
|
||||
my $lines = $messages->[1]->lineitems();
|
||||
|
||||
my $num_lines = @{$lines};
|
||||
|
||||
is( $num_lines, 8, "Correct number of lineitems returned" );
|
||||
|
||||
# sample invoice was from an early version where order was formatted basketno/ordernumber
|
||||
my $expected_ordernumber = '2818/74593';
|
||||
|
||||
my $ordernumber = $lines->[7]->ordernumber;
|
||||
|
||||
is( $ordernumber, $expected_ordernumber, 'correct ordernumber returned' );
|
||||
|
||||
my $lineprice = $lines->[7]->price_net;
|
||||
|
||||
is( $lineprice, 4.55, 'correct net line price returned' );
|
||||
|
||||
my $tax = $lines->[7]->tax;
|
||||
|
||||
is( $tax, 0, 'correct tax amount returned' );
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use FindBin qw( $Bin );
|
||||
|
||||
use Test::More tests => 34;
|
||||
|
||||
BEGIN { use_ok('Koha::Edifact') }
|
||||
|
||||
my $filename = "$Bin/edi_testfiles/prquotes_73050_20140430.CEQ";
|
||||
|
||||
my $quote = Koha::Edifact->new( { filename => $filename, } );
|
||||
|
||||
isa_ok( $quote, 'Koha::Edifact' );
|
||||
|
||||
my $x = $quote->interchange_header('sender');
|
||||
is( $x, '5013546027856', "sender returned" );
|
||||
|
||||
$x = $quote->interchange_header('recipient');
|
||||
is( $x, '5030670137480', "recipient returned" );
|
||||
$x = $quote->interchange_header('datetime');
|
||||
is( $x->[0], '140430', "datetime returned" );
|
||||
my $control_reference = 'EDIQ2857763';
|
||||
$x = $quote->interchange_header('interchange_control_reference');
|
||||
is( $x, $control_reference, "interchange_control_reference returned" );
|
||||
|
||||
$x = $quote->interchange_header('application_reference');
|
||||
is( $x, 'QUOTES', "application_reference returned" );
|
||||
|
||||
$x = $quote->interchange_trailer('interchange_control_count');
|
||||
is( $x, 1, "interchange_control_count returned" );
|
||||
|
||||
my $msgs = $quote->message_array();
|
||||
my $msg_count = @{$msgs};
|
||||
is( $msg_count, 1, "correct message count returned" );
|
||||
my $m = $msgs->[0];
|
||||
|
||||
is( $m->message_type, 'QUOTES', "Message shows correct type" );
|
||||
is( $m->message_reference_number,
|
||||
'MQ09791', "Message reference number returned" );
|
||||
is( $m->docmsg_number, 'Q741588', "Message docmsg number returned" );
|
||||
is( $m->message_date, '20140430', "Message date returned" );
|
||||
|
||||
my $lin = $m->lineitems();
|
||||
|
||||
my $num_lines = @{$lin};
|
||||
is( $num_lines, 18, 'Correct number of lines in message' );
|
||||
|
||||
my $test_line = $lin->[-1];
|
||||
|
||||
is( $test_line->line_item_number, 18, 'correct line number returned' );
|
||||
is( $test_line->item_number_id, '9780273761006', 'correct ean returned' );
|
||||
is( $test_line->quantity, 1, 'quantity returned' );
|
||||
|
||||
my $test_title = 'International business [electronic resource]';
|
||||
my $marcrec = $test_line->marc_record;
|
||||
isa_ok( $marcrec, 'MARC::Record' );
|
||||
|
||||
my $title = $test_line->title();
|
||||
|
||||
# also tests components are concatenated
|
||||
is( $title, $test_title, "Title returned" );
|
||||
|
||||
# problems currently with the record (needs leader ??)
|
||||
#is( $marcrec->title(), $test_title, "Title returned from marc");
|
||||
my $test_author = q{Rugman, Alan M.};
|
||||
is( $test_line->author, $test_author, "Author returned" );
|
||||
is( $test_line->publisher, 'Pearson Education', "Publisher returned" );
|
||||
is( $test_line->publication_date, q{2012.}, "Pub. date returned" );
|
||||
#
|
||||
# Test data encoded in GIR
|
||||
#
|
||||
my $stock_category = $test_line->girfield('stock_category');
|
||||
is( $stock_category, 'EBOOK', "stock_category returned" );
|
||||
my $branch = $test_line->girfield('branch');
|
||||
is( $branch, 'ELIB', "branch returned" );
|
||||
my $fund_allocation = $test_line->girfield('fund_allocation');
|
||||
is( $fund_allocation, '660BOO_2013', "fund_allocation returned" );
|
||||
my $collection_code = $test_line->girfield('collection_code');
|
||||
is( $collection_code, 'EBOO', "collection_code returned" );
|
||||
|
||||
#my $shelfmark = $test_line->girfield('shelfmark');
|
||||
#my $classification = $test_line->girfield('classification');
|
||||
|
||||
## text the free_text returned from the line
|
||||
my $test_line_2 = $lin->[12];
|
||||
|
||||
my $ftx_string = 'E*610.72* - additional items';
|
||||
|
||||
is( $test_line_2->orderline_free_text, $ftx_string, "ftx note retrieved" );
|
||||
|
||||
my $filename2 = "$Bin/edi_testfiles/QUOTES_413514.CEQ";
|
||||
|
||||
my $q2 = Koha::Edifact->new( { filename => $filename2, } );
|
||||
my $messages = $q2->message_array();
|
||||
|
||||
my $orderlines = $messages->[0]->lineitems();
|
||||
|
||||
my $ol = $orderlines->[0];
|
||||
|
||||
my $y = $ol->girfield( 'copy_value', 5 );
|
||||
|
||||
is( $y, undef, 'No extra item generated' );
|
||||
|
||||
$y = $ol->girfield( 'copy_value', 1 );
|
||||
is( $y, '16.99', 'Copy Price returned' );
|
||||
|
||||
$y = $ol->girfield( 'classification', 4 );
|
||||
is( $y, '914.1061', 'Copy classification returned' );
|
||||
|
||||
$y = $ol->girfield( 'fund_allocation', 4 );
|
||||
is( $y, 'REF', 'Copy fund returned' );
|
||||
|
||||
$y = $ol->girfield( 'branch', 4 );
|
||||
is( $y, 'SOU', 'Copy Branch returned' );
|
||||
|
||||
$y = $ol->girfield( 'collection_code', 4 );
|
||||
is( $y, 'ANF', 'Collection code returned' );
|
||||
|
||||
$y = $ol->girfield( 'stock_category', 4 );
|
||||
is( $y, 'RS', 'Copy stock category returned' );
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use FindBin qw( $Bin );
|
||||
|
||||
use Test::More tests => 6;
|
||||
|
||||
BEGIN { use_ok('Koha::Edifact::Order') }
|
||||
|
||||
|
||||
# The following tests are for internal methods but they could
|
||||
# error spectacularly so yest
|
||||
# Check that quoting is done correctly
|
||||
#
|
||||
my $processed_text =
|
||||
Koha::Edifact::Order::encode_text(q{string containing ?,',:,+});
|
||||
|
||||
cmp_ok(
|
||||
$processed_text, 'eq',
|
||||
q{string containing ??,?',?:,?+},
|
||||
'Outgoing text correctly quoted'
|
||||
);
|
||||
|
||||
# extend above test to test chunking in imd_segment
|
||||
#
|
||||
my $code = '010';
|
||||
my $data_to_encode = $processed_text;
|
||||
|
||||
my @segs = Koha::Edifact::Order::imd_segment( $code, $data_to_encode );
|
||||
|
||||
my $testseg = "IMD+L+010+:::$processed_text";
|
||||
$testseg .= q{'}; # add segment terminator
|
||||
|
||||
cmp_ok( $segs[0], 'eq', $testseg, 'IMD segment correctly formed' );
|
||||
|
||||
$data_to_encode = 'A' x 35;
|
||||
$data_to_encode .= 'B' x 35;
|
||||
$data_to_encode .= 'C' x 10;
|
||||
|
||||
@segs = Koha::Edifact::Order::imd_segment( $code, $data_to_encode );
|
||||
|
||||
cmp_ok(
|
||||
$segs[0],
|
||||
'eq',
|
||||
q{IMD+L+010+:::AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'},
|
||||
'IMD segment correctly chunked'
|
||||
);
|
||||
cmp_ok( $segs[1], 'eq', q{IMD+L+010+:::CCCCCCCCCC'},
|
||||
'IMD segment correctly split across segments' );
|
||||
|
||||
$data_to_encode .= '??';
|
||||
|
||||
# this used to cause an infinite loop
|
||||
@segs = Koha::Edifact::Order::imd_segment( $code, $data_to_encode );
|
||||
cmp_ok( $segs[1], 'eq', q{IMD+L+010+:::CCCCCCCCCC??'},
|
||||
'IMD segment deals with quoted character at end' );
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use FindBin qw( $Bin );
|
||||
|
||||
use Test::More tests => 16;
|
||||
|
||||
BEGIN { use_ok('Koha::Edifact') }
|
||||
|
||||
my $filedir = "$Bin/edi_testfiles";
|
||||
|
||||
my @files = map { "$filedir/$_" }
|
||||
( 'ordrsp1.CEA', 'ordrsp2.CEA', 'ordrsp3.CEA', 'ordrsp4.CEA' );
|
||||
|
||||
my @responses;
|
||||
for my $filename (@files) {
|
||||
|
||||
my $order_response = Koha::Edifact->new( { filename => $filename, } );
|
||||
|
||||
isa_ok( $order_response, 'Koha::Edifact' );
|
||||
push @responses, $order_response;
|
||||
}
|
||||
|
||||
# tests on file 1
|
||||
# Order accepted with amendments
|
||||
my $order_response = $responses[0];
|
||||
|
||||
my $msg = $order_response->message_array();
|
||||
my $no_of_msg = @{$msg};
|
||||
is( $no_of_msg, 1, "Correct number of messages returned" );
|
||||
|
||||
isa_ok( $msg->[0], 'Koha::Edifact::Message' );
|
||||
|
||||
my $lines = $msg->[0]->lineitems();
|
||||
|
||||
my $no_of_lines = @{$lines};
|
||||
|
||||
is( $no_of_lines, 3, "Correct number of orderlines returned" );
|
||||
|
||||
#
|
||||
is( $lines->[0]->ordernumber(), 'P28837', 'Line 1 correct ordernumber' );
|
||||
is(
|
||||
$lines->[0]->coded_orderline_text(),
|
||||
'Not yet published',
|
||||
'NP returned and translated'
|
||||
);
|
||||
|
||||
is( $lines->[1]->ordernumber(), 'P28838', 'Line 2 correct ordernumber' );
|
||||
is( $lines->[1]->action_notification(),
|
||||
'cancelled', 'Cancelled action returned' );
|
||||
is( $lines->[1]->coded_orderline_text(),
|
||||
'Out of print', 'OP returned and translated' );
|
||||
|
||||
is( $lines->[2]->ordernumber(), 'P28846', 'Line 3 correct ordernumber' );
|
||||
is( $lines->[2]->action_notification(),
|
||||
'recorded', 'Accepted with change action returned' );
|
||||
|
||||
is( $lines->[0]->availability_date(), '19971120',
|
||||
'Availability date returned' );
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
|||
UNA:+.? 'UNB+UNOC:3+4012345000094+5412345000176+140430:1849+EDIQ2857776++ORDRSP'UNH+ME001234+ORDRSP:D:96A:UN:EAN005'BGM+231+R967634+4'DTM+137:19971028:102'NAD+BY+5412345000176::9'NAD+SU+4012345000094::9'CUX+2:GBP:9'LIN+1+24'PIA+5+0316907235:IB'QTY+21:2'QTY+83:2'DTM+44:19971120:102'FTX+LIN++NP:8B:28'PRI+AAE:15.99:CA:SRP'RFF+LI:P28837'LIN+2+2'PIA+5+0856674427:IB'QTY+21:1'FTX+LIN++OP:8B:28'RFF+LI:P28838'LIN+3+24'PIA+5+0870701436:IB'PIA+3+0870701428:IB'QTY+21:1'FTX+LIN++OP:8B:28'PRI+AAE:25:CA:SRP'RFF+LI:P28846'UNS+S'CNT+2:3'UNT+29+ME001234'UNZ+1+EDIQ2857776'
|
||||
|
|
@ -0,0 +1 @@
|
|||
UNA:+.? 'UNB+UNOC:3+4012345000094+5412345000176+140430:1849+EDIQ2857775++ORDRSP'UNH+ME001235+ORDRSP:D:96A:UN:EAN005'BGM+231+R967635+27'DTM+137:19971028:102'FTX+GEN++ACS:9B:28'RFF+ON:H67209'NAD+BY+5412345000176::9'NAD+SU+4012345000094::9'UNS+S'CNT+2:0'UNT+10+ME001235'UNZ+1+EDIQ2857775'
|
||||
|
|
@ -0,0 +1 @@
|
|||
UNA:+.? 'UNB+UNOC:3+4012345000094+5412345000176+140430:1849+EDIQ2857774++ORDRSP'UNH+ME001236+ORDRSP:D:96A:UN:EAN005'BGM+23C+R967635+4'DTM+137:20010624:102'NAD+BY+5412345000176::9'NAD+SU+4012345000094::9'LIN+1+4'PIA+5+00076511236:IB'QTY+21:3’GIR+001+5346:LCO+1000431:LAC+FIC:LFN+AN:LLO'GIR+002+5347:LCO+1000432:LAC+FIC:LFN+AN:LLO'GIR+003+5348:LCO+1000433:LAC+FIC:LFN+BB:LLO'RFF+LI:0190045'LIN+2+4’PIA+5+0863183913:IB'QTY+21:1'GIR+001+6210:LCO+1000434:LAC+FIC:LFN+BB:LLO+398:LCL'GIR+001+JON:LFS+14DAY:LLN'RFF+LI:0190056'UNS+S'CNT+2:2'UNT+21:ME001236'UNZ+1+EDIQ2857774'
|
||||
|
|
@ -0,0 +1 @@
|
|||
UNA:+.? 'UNB+UNOC:3+4012345000094+5412345000176+140430:1849+EDIQ2857773++ORDRSP'UNH+ME001345+ORDRSP:D:96A:UN:EAN005'BGM+23C+F964312+4'DTM+137:20010430:102'NAD+BY+5412345000176::9'NAD+SU+4012345000094::9'LIN+1+4'PIA+5+0007107781:IB'QTY+21:4'GIR+L01+214365:LAC+214366:LAC+DA:LLO+920:LCL+SEC:LFS'GIR+L01+NFIC:LFS'GIR+L02+214367:LAC+214368:LAC+FG:LLO+920:LCL+SEC:LFS'GIR+L02+NFIC:LFS'RFF+LI:0184364’LOC+7+BR1::92'QTY+11:2'LOC+20+FG::92'QTY+11:2'UNS+S'CNT+2:1'UNT+20:ME001345'UNZ+1+EDIQ2857773'
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue