LP#1373690: normalize IMD values better
authorGalen Charlton <gmc@equinoxinitiative.org>
Tue, 22 Aug 2017 20:00:47 +0000 (16:00 -0400)
committerMike Rylander <mrylander@gmail.com>
Fri, 1 Sep 2017 17:13:31 +0000 (13:13 -0400)
The EDItEUR book ORDERS message allows two 35-character
item description data elements in IMD segments; this patch
accounts for that. It also, for IMD fields, uses the EDIFACT
release character to escape certain characters, better matching
how the Ruby EDI translator did it.

Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Open-ILS/src/perlmods/lib/OpenILS/Utils/EDIWriter.pm

index 2b94467..3b4cdfd 100644 (file)
@@ -65,7 +65,16 @@ sub get_po {
     ]);
 }
 
-sub escape_edi {
+sub add_release_characters {
+    my ($self, $value) = @_;
+    return '' if (not defined $value || ref($value));
+
+    # escape ? ' + : with the release character ?
+    $value =~ s/([\?'\+:])/?$1/g;
+
+    return $value;
+}
+sub escape_edi_imd {
     my ($self, $value) = @_;
     return '' if (not defined $value || ref($value));
 
@@ -79,16 +88,28 @@ sub escape_edi {
     # LP #812593).
     $value =~ s/[\[\]]//g;
 
-    # Characters [? + ' \ : <newline>] are all potentially problematic for 
+    # Characters [\ <newline>] are all potentially problematic for 
     # EDI messages, regardless of their position in the string.
-    # Safest to simply remove them.
-    $value =~ s/[\\\?\+':]//g;
+    # Safest to simply remove them. Note that unlike escape_edi(),
+    # we're not stripping out +, ', :, and + because we'll escape
+    # them when buidling IMD segments
+    $value =~ s/[\\]//g;
 
     # Replace newlines with spaces.
     $value =~ s/\n/ /g;
 
     return $value;
 }
+sub escape_edi {
+    my ($self, $value) = @_;
+
+    my $str = $self->escape_edi_imd($value);
+
+    # further strip + ' : +
+    $str =~ s/[\?\+':]//g;
+
+    return $str;
+}
 
 # Returns an EDI-escaped version of the requested lineitem attribute
 # value.  If $attr_type is not set, the first attribute found matching 
@@ -105,6 +126,20 @@ sub get_li_attr {
     return '';
 }
 
+# Like get_li_attr, but don't strip out ? + : ' as we'll
+# escape them later
+sub get_li_attr_imd {
+    my ($self, $li, $attr_name, $attr_type) = @_;
+
+    for my $attr (@{$li->attributes}) {
+        next unless $attr->attr_name eq $attr_name;
+        next if $attr_type && $attr->attr_type ne $attr_type;
+        return $self->escape_edi_imd($attr->attr_value);
+    }
+
+    return '';
+}
+
 # Generates a HASH version of the PO with all of the data necessary
 # to generate an EDI message from the PO.
 sub compile_po {
@@ -264,7 +299,7 @@ sub compile_li {
     $self->set_li_order_ident($li, $li_hash);
 
     for my $name (qw/title author edition pubdate publisher pagination/) {
-        $li_hash->{$name} = $self->get_li_attr($li, $name);
+        $li_hash->{$name} = $self->get_li_attr_imd($li, $name);
     }
 
     $self->compile_copies($li, $li_hash);
@@ -345,8 +380,8 @@ sub compile_copy {
     });
 }
 
-# IMD fields are limited to 70 chars per value.  Any values longer
-# should be carried via repeating IMD fields.
+# IMD fields are limited to 70 chars per value over two DEs.
+# Any values longer # should be carried via repeating IMD fields.
 # IMD fields should only display the +::: when a value is present
 sub IMD {
     my ($self, $code, $value) = @_;
@@ -359,7 +394,16 @@ sub IMD {
     if ($value) {
         my $s = '';
         for my $part ($value =~ m/.{1,70}/g) {
-            $s .= "IMD+F+$code+:::$part'\n"; }
+            my $de;
+            if (length($part) > 35) {
+                $de = $self->add_release_characters(substr($part, 0, 35)) .
+                      ':' .
+                      $self->add_release_characters(substr($part, 35));
+            } else {
+                $de = $self->add_release_characters($part);
+            }
+            $s .= "IMD+F+$code+:::$de'\n";
+        }
         return $s;
 
     } else {