Class: EDI::E::Message

Inherits:
Message show all
Defined in:
lib/edi4r/edifact.rb

Overview

Class EDI::E::Message

This class implements a single business document according to UN/EDIFACT

Constant Summary collapse

@@message_defaults =

private_class_method :new

{
  :msg_type => 'ORDERS', :version => 'D', :release => '96A',
  :resp_agency => 'UN', :assigned_code => nil # e.g. 'EAN008'
}
@@message_default_keys =

e.g. 'EAN008'

@@message_defaults.keys

Instance Attribute Summary

Attributes inherited from Collection_HT

#header, #trailer

Attributes inherited from Object

#name, #parent, #root

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Message

#each_BCDS, #fmt_of_DE, parse_xml, #to_xml

Methods inherited from Collection_HT

#empty?, #root=, #to_din16557_4, #to_xml, #to_xml_header, #to_xml_trailer

Methods inherited from Collection

#==, #[], #each, #find_all, #first, #index, #inspect, #last, #length, #map, #names, #normalized_class_name, #root=, #size

Constructor Details

#initialize(p, user_par = {}) ⇒ Message

Creates an empty UN/EDIFACT message Don't use directly - use new_message of class Interchange or MsgGroup instead!

First parameter

This is always the parent object, either a message group or an interchange object. Use method new_message in the corresponding object instead of creating messages unattended, and the parent reference will be accounted for automatically.

Second parameter, case “Hash”

List of supported hash keys:

Essentials, should not be changed later

:msg_type

Sets S009.0065, default = 'ORDERS'

:version

Sets S009.0052, default = 'D'

:release

Sets S009.0054, default = '96A'

:resp_agency

Sets S009.0051, default = 'UN'

Optional parameters, required depending upon use case

:assigned_code

Sets S009.0057 (subset), default = nil

Second parameter, case “Segment”

This mode is only used internally when parsing data.

Notes

  • The counter in UNH (0062) is set automatically to a number that is unique for the running process.

  • The trailer segment (usually UNT) is generated automatically.

  • Whenever possible, avoid write access to the message header or trailer segments!


1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
# File 'lib/edi4r/edifact.rb', line 1101

def initialize( p, user_par={} )
  super( p, user_par )

  # First param is either a hash or segment UNH
  # - If Hash:    Build UNH from given parameters
  # - If Segment: Extract some crucial parameters
  if user_par.is_a? Hash
    preset_msg( user_par )
    par = {
      :d0065 => @name, :d0052=> @version, :d0054=> @release,
      :d0051 => @resp_agency, :d0057 => @subset, :is_iedi => root.is_iedi?
    }
    @maindata = EDI::Dir::Directory.create(root.syntax, par )

    if root.is_iedi?
      @header = new_segment('UIH')
      @trailer = new_segment('UIT')
      cde = @header.cS306
#          cde.d0113 = @sub_id
      @header.d0340 = p.messages_created
    else
      @header = new_segment('UNH')
      @trailer = new_segment('UNT')
      cde = @header.cS009
      @header.d0062 = p.messages_created
    end
    cde.d0065 = @name
    cde.d0052 = @version
    cde.d0054 = @release
    cde.d0051 = @resp_agency
    cde.d0057 = @subset

  elsif user_par.is_a? Segment
    @header = user_par
    raise "UNH expected, #{@header.name} found!" if @header.name != 'UNH'
    # I-EDI support to be added!
    @header.parent = self
    @header.root = self.root
    @trailer = Segment.new(root, 'UNT') # temporary
    s009 = @header.cS009
    @name = s009.d0065
    @version = s009.d0052
    @release = s009.d0054
    @resp_agency = s009.d0051
    @subset = s009.d0057
    par = {
      :d0065 => @name, :d0052=> @version, :d0054=> @release,
      :d0051 => @resp_agency, :d0057 => @subset, :is_iedi => root.is_iedi?
    }
    @maindata = EDI::Dir::Directory.create(root.syntax, par )
  else
    raise "First parameter: Illegal type!"
  end

  @trailer.d0074 = 2 if @trailer  # Just UNH and UNT so far
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class EDI::Collection

Class Method Details

.parse(parent, segment_list) ⇒ Object

Returns a new Message object that contains the data of the strings passed in the segment_list array. Uses the context of the given parent object and configures message as a child.


1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
# File 'lib/edi4r/edifact.rb', line 1215

def Message.parse (parent, segment_list)

  if parent.root.is_iedi?
    h, t, re_t = 'UIH', 'UIT', /^UIT/
  else
    h, t, re_t = 'UNH', 'UNT', /^UNT/
  end

  # Segments comprise a single message
  # Temporarily assign a parent, or else service segment lookup fails
  header  = parent.parse_segment(segment_list.shift, h)
  msg     = parent.new_message(header)
  trailer = msg.parse_segment( segment_list.pop, t )

  segment_list.each do |segbuf|
    seg = Segment.parse( msg, segbuf )
    if segbuf =~ re_t # FIXME: Should that case ever occur?
      msg.trailer = seg
    else
      msg.add(seg)
    end
  end
  msg.trailer = trailer
  msg
end

Instance Method Details

#add(seg) ⇒ Object

Add a previously derived segment to the end of this message (append) Make sure that all mandatory elements have been supplied.

Notes

  • Strictly add segments in the sequence described by this message's branching diagram!

  • Adding a segment will automatically increase the corresponding counter in the message trailer.

Example:

seg = msg.new_segment( 'BGM' )
seg.d1004 = '220'
# etc.
msg.add seg

1260
1261
1262
1263
1264
# File 'lib/edi4r/edifact.rb', line 1260

def add( seg )
  super
  @trailer.d0074 = @trailer.d0074.to_i if @trailer.d0074.is_a? String
  @trailer.d0074 += 1	# What if new segment is/remains empty??
end

#new_segment(tag) ⇒ Object

Derive a new segment with the given name from this message context. The call will fail if the message name is unknown to this message's UN/TDID (not in EDMD/IDMD).

Example:

seg = msg.new_segment( 'BGM' )
seg.d1004 = '220'
# etc.
msg.add seg

1169
1170
1171
# File 'lib/edi4r/edifact.rb', line 1169

def new_segment( tag )
  Segment.new(self, tag)
end

#parse_segment(buf, tag) ⇒ Object

Internal use only!


1175
1176
1177
# File 'lib/edi4r/edifact.rb', line 1175

def parse_segment(buf, tag) # :nodoc:
  Segment.parse(self, buf, tag)
end

#preset_msg(user_par) ⇒ Object

Internal use only!


1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
# File 'lib/edi4r/edifact.rb', line 1181

def preset_msg(user_par) # :nodoc:
  if (illegal_keys = user_par.keys - @@message_default_keys) != []
    msg = "Illegal parameter(s) found: #{illegal_keys.join(', ')}\n"
    msg += "Valid param keys (symbols): #{@@message_default_keys.join(', ')}"
    raise ArgumentError, msg
  end

  # Use UNG as source for defaults if present
  ung = parent.header
  if parent.is_a?(MsgGroup) && ung.d0038
    s008 = ung.cS008
    par = {
      :msg_type=> ung.d0038, :version=> s008.d0052, :release=> s008.d0054,
      :resp_agency => ung.d0051, :assigned_code => s008.d0057
    }.merge( user_par )
  else
    par = @@message_defaults.merge( user_par )
  end

  @name = par[:msg_type]
  @version = par[:version]
  @release = par[:release]
  @resp_agency = par[:resp_agency]
  @subset = par[:assigned_code]
  # FIXME: Eliminate use of @version, @release, @resp_agency, @subset
  #        They get outdated whenever their UNH counterparts are changed
  #        Try to keep @name updated, or pass it a generic name
end

#to_sObject


1346
1347
1348
1349
# File 'lib/edi4r/edifact.rb', line 1346

def to_s
  postfix = '' << root.una.seg_term << root.e_linebreak
  super( postfix )
end

#validate(err_count = 0) ⇒ Object


1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
# File 'lib/edi4r/edifact.rb', line 1267

def validate( err_count=0 )
  # Check sequence of segments against library,
  # thereby adding location information to each segment

  par = {
    :d0065 => @name, :d0052=> @version, :d0054=> @release,
    :d0051 => @resp_agency, :d0057 => @subset,
    :d0002 => root.version, :is_iedi => root.is_iedi?,
    :d0076 => nil  # SV 4-1 support still missing here
  }
  diag = EDI::Diagram::Diagram.create( root.syntax, par )
  ni = EDI::Diagram::NodeInstance.new(diag)

  ni.seek!( @header )
  @header.update_with( ni )
  each do |seg|
    if ni.seek!(seg)
      seg.update_with( ni )
    else
      # FIXME: Do we really have to fail here, or would a "warn" suffice?
      raise "seek! failed for #{seg.name} when starting at #{ni.name}"
    end
  end
  ni.seek!( @trailer )
  @trailer.update_with( ni )


  # Consistency checks

  if (a=@trailer.d0074) != (b=self.size+2)
    warn "DE 0074 (#{a}) does not match number of segments (#{b})"
    err_count += 1
  end

  if root.is_iedi?
    a, b = @trailer.d0340, @header.d0340
  else
    a, b = @trailer.d0062, @header.d0062
  end
  if a != b
    warn "Trailer reference (#{a}) does not match header reference (#{b})"
    err_count += 1
  end

  if parent.is_a? MsgGroup
    ung = parent.header; s008 = ung.cS008; s009 = header.cS009
    a, b = s009.d0065, ung.d0038
    if a != b
      warn "Message type (#{a}) does not match that of group (#{b})"
      err_count += 1
    end
    a, b = s009.d0052, s008.d0052
    if a != b
      warn "Message version (#{a}) does not match that of group (#{b})"
      err_count += 1
    end
    a, b = s009.d0054, s008.d0054
    if a != b
      warn "Message release (#{a}) does not match that of group (#{b})"
      err_count += 1
    end
    a, b = s009.d0051, ung.d0051
    if a != b
      warn "Message responsible agency (#{a}) does not match that of group (#{b})"
      err_count += 1
    end
    a, b = s009.d0057, s008.d0057
    if a != b
      warn "Message association assigned code (#{a}) does not match that of group (#{b})"
      err_count += 1
    end

  end

  # Now check each segment
  super( err_count )
end