kputnam / stupidedi

Ruby API for parsing and generating ASC X12 EDI transactions
BSD 3-Clause "New" or "Revised" License
262 stars 137 forks source link

Segment cannot be reached from the current state #16

Closed Shpigford closed 11 years ago

Shpigford commented 11 years ago

I'm getting this error:

Stupidedi::Exceptions::ParseError: AT7 segment cannot be reached from the current state

When I run this:

a.flatmap{|m| m.sequence(:GS, :ST) }.tap do |m|
  m.find(:AT7)
end

Here's an example transaction set:

ST|214|000056391~
B10|0|0|UPSN~
L11|QVD|TN|O4~
L11|1Z6VX489YN03535798|2I~
N1|SH|NA|25|6VX489~
N1|BT||25|6VX489|01~
LX|1~
AT7|X8|AJ|||20130612|232947|LT~
AT7|||AB|BG|20130612~
L11|2U-UPS WILL ATTEMPT DELIVERY|REC|KAGE WILL NOT BE TRANSFERRED T~
MAN|CP|E1|OKC EDMOND|CP|OK|US~
CD3|||R4~
N1|UP|NA~
SE|14|000056391~

If I run that find on other elements (like L11 or N1) it works fine, but AT7 and MAN throw that error.

kputnam commented 11 years ago

Take a closer look at the grammar for QM 214, it seems the parent segment of AT7 is LX. You'll need to move the cursor to an LX segment before you can jump to AT7. Try m.sequence(:GS, :ST, :LX, :AT7) to prove that it works -- though you will probably want to use something like

m.iterate(:ISA) do |m|
  m.iterate(:GS, "QM") do |m|
    m.iterate(:ST, "214") do |m|
      m.iterate(:LX) do |m|
        m.iterate(:AT7) do |m|
          ...

Because your file may have many ISAs, and many GS_QMs within each ISA, and many ST_214 within each GS, and many LXs within each ST, etc. Using m.sequence(:GS, ST, :LX, :AT7) is the same as m.find(:GS).flatmap{|m| m.find(:ST) }.flatmap{|m| m.find(:LX) } etc which moves to the nearest GS, then to the nearest ST from there, then the nearest LX from there, etc. If there were many GS segments, you'd only visit one.

Lastly, I don't see a MAN segment specified in the grammar for QM 214 so stupidedi doesn't know where to look for one in the parse tree. I'd suggest copying the two files named QM214 in lib/stupidedi/contrib and naming them like GRPN-QM214 and nesting them in a GRPN namespace. Then specify the MAN segment in both files (you'll probably need some documentation from your trading partner), and wire up the config in Stupidedi::Config.contrib.

Shpigford commented 11 years ago

Gotcha.That worked.

So, I've created those new files...but am a little lost about exactly how to properly "nest them in a GRPN namespace" and "wire up the config".

kputnam commented 11 years ago

Good! What I mean is that your QM214 will be a variation on the existing one, and it seems likely that both are "correct" for their intended trading partners. So to preserve the existing one, you should create your version in another namespace.

Here's what I suggest creating (or modifying). I assume GRPN or UPSS is your trading partner based on your earlier sample file. Choose whatever namespace is meaningful to you, though.

_stupidedi/contrib/004010/transaction_setdefs.rb

module Stupidedi
  module Contrib
    module FortyTen
      module TransactionSetDefs
...
        module GRPN
          autoload :QM214,
            "stupiedi/contrib/004010/transaction_set_defs/grpn/QM214.rb"
        end
...

_stupiedi/contrib/004010/transaction_setdefs/grpn/QM214.rb

module Stupidedi
  module Contrib
    module FortyTen
      module TransactionSetDefs
        module GRPN
          QM214 = ...

stupidedi/contrib/004010/guides.rb

module Stupidedi
  module Contrib
    module FortyTen
      module Guides
...
        module GRPN
          autoload :QM214,
            "stupdedi/contrib/004010/guides/grpn/QM214"
        end
...

stupdedi/contrib/004010/guides/grpn/QM214.rb

module Stupidedi
  module Contrib
    module FortyTen
      module Guides
        module GRPN
          QM214 = ...

Then somewhere in your application, to configure the library to use these grammars, you'll do this:

config = Stupidedi::Config.default
config.customize do |c|
  c.transaction_set.customize do |x|
    x.register("004010", "QM", "214") { Stupidedi::Contrib::FortyTen::Guides::GPRN::QM214 }
  end
end
Shpigford commented 11 years ago

Makes sense.

Is there anywhere else I need to add new segments than those two files?

I'm getting this error on a new one I added:

NameError: uninitialized constant Stupidedi::Versions::FunctionalGroups::FortyTen::SegmentDefs::CD3

On this line:

s::CD3.use(210, r::Optional, d::RepeatCount.bounded(1)),

That matches up to this line in the guides file:

b::Segment(210, s::CD3, "Carton (Package) Detail (Manifest and Void Manifest Scans)",

The error gets thrown when running parser.read(Stupidedi::Reader.build(input))

kputnam commented 11 years ago

Yes. Not all segments were included in the library (there are hundreds), but you can add them. These should go exactly where the error mentions: Stupidedi::Versions::FunctionalGroups::FortyTen::SegmentDefs::CD3.

Take a look at lib/stupidedi/versions/functional_groups/004010/segment_defs/PER.rb as an example, and then look how PER is written in this spec. That should get you in the right direction to extrapolate the SegmentDef for CD3. You might check the linked spec with whatever documentation you have (I'm only hoping it's correct).

You may also run into undefined elements like E187 or something. Those are defined in 004010/element_defs.rb and you can find documentation on those here.

Shpigford commented 11 years ago

Annnnd I'm beginning to fully understand the name of this gem. The depths at which EDI's stupidity extends is hurting my brain. :)

This helps a ton, really appreciate your help on all of this.