edi4r 0.9.4.1 → 0.9.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/AuthorCopyright +3 -3
  3. data/{ChangeLog → Changelog} +60 -0
  4. data/README +15 -10
  5. data/Tutorial +2 -3
  6. data/VERSION +1 -1
  7. data/bin/edi2xml.rb +12 -16
  8. data/bin/editool.rb +9 -5
  9. data/bin/sedas2eancom02.rb +1385 -0
  10. data/bin/xml2edi.rb +7 -12
  11. data/data/edifact/iso9735/SDCD.20000.csv +1 -0
  12. data/data/edifact/iso9735/SDCD.3for2.csv +1 -0
  13. data/data/edifact/iso9735/SDED.20000.csv +6 -0
  14. data/data/edifact/iso9735/SDED.30000.csv +43 -43
  15. data/data/edifact/iso9735/SDED.3for2.csv +6 -0
  16. data/data/edifact/iso9735/SDED.40000.csv +129 -129
  17. data/data/edifact/iso9735/SDED.40100.csv +130 -130
  18. data/data/edifact/iso9735/SDMD.20000.csv +6 -0
  19. data/data/edifact/iso9735/SDMD.30000.csv +6 -6
  20. data/data/edifact/iso9735/SDMD.3for2.csv +6 -0
  21. data/data/edifact/iso9735/SDMD.40000.csv +17 -17
  22. data/data/edifact/iso9735/SDMD.40100.csv +17 -17
  23. data/data/edifact/iso9735/SDSD.20000.csv +5 -0
  24. data/data/edifact/iso9735/SDSD.3for2.csv +5 -0
  25. data/data/edifact/untdid/EDMD.d01b.csv +1 -1
  26. data/data/sedas/EDCD..csv +0 -0
  27. data/data/sedas/EDED..csv +859 -0
  28. data/data/sedas/EDMD..csv +16 -0
  29. data/data/sedas/EDSD..csv +44 -0
  30. data/lib/edi4r.rb +147 -67
  31. data/lib/edi4r/ansi_x12-rexml.rb +91 -0
  32. data/lib/edi4r/ansi_x12.rb +1684 -0
  33. data/lib/edi4r/diagrams.rb +75 -14
  34. data/lib/edi4r/edifact-rexml.rb +4 -3
  35. data/lib/edi4r/edifact.rb +505 -202
  36. data/lib/edi4r/rexml.rb +13 -7
  37. data/lib/edi4r/sedas.rb +854 -0
  38. data/lib/edi4r/standards.rb +150 -33
  39. data/test/damaged_file.edi +1 -0
  40. data/test/eancom2webedi.rb +1 -0
  41. data/test/groups.edi +1 -1
  42. data/test/test_basics.rb +16 -9
  43. data/test/test_edi_split.rb +30 -0
  44. data/test/test_loopback.rb +7 -2
  45. data/test/test_rexml.rb +34 -2
  46. data/test/test_service_messages.rb +190 -0
  47. data/test/test_streaming.rb +167 -0
  48. data/test/test_tut_examples.rb +3 -1
  49. data/test/webedi2eancom.rb +1 -0
  50. metadata +121 -77
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 991135e53b123b05465006a77510f27a07315f26
4
+ data.tar.gz: 3c08cc53d955354fc119c52589a5a86e0a51e6bb
5
+ SHA512:
6
+ metadata.gz: 0bc5dd2fec9955e8549b5553280fcbe56066c35a1648299324405be92b96b99598f5da24978e41487be2cd9be95c1e9f67746d0a31cb7a5186a326c1d095c286
7
+ data.tar.gz: 6a20a3bad4528be17d24ddef2c7e873c3bb45d2e55b327deaed82e51d562b04b5ebcaf47752f5bf7242f1a5743c8116d786fedd0b2e0f5788d4582bf73b5c938
@@ -1,10 +1,10 @@
1
1
  == Author
2
2
 
3
- Heinz W. Werntges, FH Wiesbaden
4
- (edi@informatik.fh-wiesbaden.de)
3
+ Heinz W. Werntges, RheinMain University of Applied Sciences, Wiesbaden
4
+ (edi-ai-dcsm@hs-rm.de)
5
5
 
6
6
  == Copyright
7
7
 
8
- Copyright (c) 2006 Heinz W. Werntges.
8
+ Copyright (c) 2006, 2015 Heinz W. Werntges.
9
9
  Licensed under the same terms as Ruby.
10
10
 
@@ -1,5 +1,65 @@
1
1
  = Change log
2
2
 
3
+ == 0.9.6.2
4
+ === Tested with Ruby 2.2.1, some RDoc links fixed
5
+
6
+ == 0.9.6.1
7
+
8
+ === Service messages, subset support
9
+ * Master data for CONTRL, AUTACK and KEYMAN fixed now, new test added
10
+ * Master data fix: Elimination of non-ASCII chars so Ruby 1.9 does not need -E
11
+ * Subsets now tolerated (warning & fallback to plain EDIFACT)
12
+ * More support for subset EANCOM (prepared for master data)
13
+
14
+ == 0.9.6.0
15
+
16
+ === Adjustments for Ruby 1.9.2
17
+ * Encodings added, minor patches required, some typos fixed
18
+ * Some dead files removed
19
+ * XML: FPI in doctype declaration adapted to new organization name
20
+ * Tests: Rakefile replaces Makefile, works for both Ruby 1.8 & 1.9 now
21
+
22
+ == 0.9.5.3
23
+
24
+ === Adjustments for Ruby 1.8.7
25
+ * Changed when cond: ... to when cond then ...
26
+ * Removed "break" from a case/when block
27
+ * Corrected expectations in tests for edi_split
28
+ * Adjusted a test that calls ruby on the cmd line to accept versions (prep for 1.9.x)
29
+
30
+ == 0.9.5.2
31
+
32
+ === Misc improvements & bug fixes
33
+ * Two bugs fixed re. ANSI msg and msg_group initialization
34
+ * ANSI getters/setters "rXX" in test use now.
35
+ * Support for 270, 271, 276, 277 added, including test cases
36
+
37
+ == 0.9.5.1
38
+
39
+ === News
40
+ * First limited support for UN/EDIFACT Subsets
41
+ * Basic support for SEDAS (as source, not target standard)
42
+ * Basic support for ANSI X12 (just 837, 997 for V4.01)
43
+ * Support for processing arbitrarily large interchanges through classes
44
+ EDI::E::StreamingParser and StreamingBuilder
45
+ * New testcase: test_streaming.rb (large, proprietary testfiles not provided)
46
+ * peek(): Now featuring ":deep_peek" (group and message structure)
47
+ * 0.9.5.1: X12 835 supported, test suite: x12 cases added
48
+
49
+ === Changes
50
+ * Interchange.peek: Now supporting parameters. New: deep_peek
51
+ * Interchange.parse & Interchange.peek re-implemented based on StreamingBuilder
52
+ * MsgGroup::E::add() may now prevent auto-validation
53
+ * Class EDI::Time introduced, base class Time not modified anymore
54
+
55
+ === Misc improvements & bug fixes
56
+ * EDI::Bzip2Reader::getc() added (needed by new streaming parser)
57
+ * EDI::Collection::find() added
58
+ * EDI::Collection::[]: Now accepting also Regex objects
59
+ * standards.rb: EDIFACT version & release: to_s enforced (bug report by Marius Z., SV1 issue)
60
+ * 0.9.5.1: EDI::logger available now!
61
+
62
+
3
63
  == 0.9.4.1
4
64
 
5
65
  === News
data/README CHANGED
@@ -3,9 +3,10 @@
3
3
  This is Ruby gem <b>edi4r</b> version
4
4
  :include:VERSION
5
5
 
6
- Edi4r was created to greatly simplify the creation and processing of data for
7
- Electronic Data Interchange (EDI). In particular, it supports the UN/EDIFACT
8
- syntax (ISO 9573) and optionally SAP IDocs.
6
+ Edi4r was created to greatly simplify the creation and processing of data
7
+ for Electronic Data Interchange (EDI). In particular, it supports the
8
+ UN/EDIFACT syntax (ISO 9573) and optionally SAP IDocs.
9
+ Limited support is offered for ANSI X.12 and SEDAS.
9
10
 
10
11
  == Installation
11
12
 
@@ -13,10 +14,14 @@ Install it as any other Ruby gem, e.g.:
13
14
 
14
15
  sudo gem install edi4r-<version>.gem
15
16
 
17
+ With administrator privileges and network access, just type:
18
+
19
+ gem install edi4r
20
+
21
+
16
22
  == Usage
17
23
 
18
- require 'rubygems'
19
- require_gem 'edi4r'
24
+ require 'edi4r' # Older installations: require_gem 'edi4r'
20
25
  require 'edi4r/edifact' # optional
21
26
 
22
27
  # Build a UN/EDIFACT interchange from its character representation in a file:
@@ -49,15 +54,15 @@ Install it as any other Ruby gem, e.g.:
49
54
 
50
55
  == See also
51
56
 
52
- * Background[link:files/lib/edi4r_rb.html] info about data structure and classes.
57
+ * Background[link:EDI.html] info about data structure and classes.
53
58
 
54
- * A Tutorial[link:files/Tutorial.html] for examples of use.
59
+ * A Tutorial[link:Tutorial.html] for examples of use.
55
60
 
56
- * A ChangeLog[link:files/ChangeLog.html] will be maintained; it is just starting now.
61
+ * A Changelog[link:Changelog.html] is maintained since version 0.8
57
62
 
58
- * Finally, see TO-DO[link:files/TO-DO.html] for the current wish list.
63
+ * Finally, see TO-DO[link:TO-DO.html] for the current wish list.
59
64
 
60
- * This code is put under the Ruby license, see COPYING[link:files/COPYING.html] for details.
65
+ * This code is put under the Ruby license, see COPYING[link:COPYING.html] for details.
61
66
 
62
67
  :include:AuthorCopyright
63
68
 
data/Tutorial CHANGED
@@ -9,7 +9,6 @@
9
9
 
10
10
  === Require statements
11
11
 
12
- require "rubygems"
13
12
  require "edi4r" # Try require_gem if this fails on your site
14
13
  require "edi4r/edifact"
15
14
  require "edi4r-tdid" # Try require_gem if this fails on your site
@@ -58,7 +57,7 @@ at any other location. If you need to do that, hold your messages
58
57
  in an array, sort them any way you like, and finally add them
59
58
  to the interchange in the desired sequence.
60
59
 
61
- Note that each messag gets validated by default when you add it to
60
+ Note that each message gets validated by default when you add it to
62
61
  the interchange. If your message needs to be completed only later,
63
62
  you may disable validation by calling:
64
63
 
@@ -596,7 +595,7 @@ To be supplied later; currently just a room to collect keywords to cover.
596
595
  Segments: T-nodes, ordinal number, index, occurrence, max. occurrence
597
596
  empty?, required?
598
597
 
599
- === More advances features
598
+ === More advanced features
600
599
 
601
600
  Validation: warnings and exceptions (logging?)
602
601
  Add-ons to class Time
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.4.1
1
+ 0.9.6.2
@@ -1,17 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
-
3
- # $Id: edi2xml.rb,v 1.1 2006/08/01 11:20:24 werntges Exp $
4
-
5
- # $Log: edi2xml.rb,v $
6
- # Revision 1.1 2006/08/01 11:20:24 werntges
7
- # Initial revision
8
- #
2
+ # -*- encoding: ISO-8859-1 -*-
9
3
  #
10
- # Author: Heinz W. Werntges (edi@informatik.fh-wiesbaden.de)
4
+ # Author: Heinz W. Werntges (edi@cs.hs-rm.de)
11
5
  #
12
6
  # License: This code is put under the Ruby license
13
7
  #
14
- # Copyright (c) 2006 Heinz W. Werntges, FH Wiesbaden
8
+ # Copyright (c) 2006, 2011 Heinz W. Werntges, Hochschule RheinMain, Wiesbaden
15
9
  #
16
10
 
17
11
  # SYNOPSIS:
@@ -30,10 +24,12 @@
30
24
  #
31
25
 
32
26
  require "rubygems"
33
- require_gem "edi4r"
34
- require_gem "edi4r-tdid"
35
- #require_gem "edi4r-idoc"
27
+ require "edi4r"
28
+ require "edi4r-tdid"
29
+ #require "edi4r-idoc"
36
30
  require "edi4r/edifact"
31
+ require "edi4r/ansi_x12"
32
+ require "edi4r/sedas"
37
33
  require "edi4r/rexml"
38
34
 
39
35
  require "getoptlong"
@@ -44,7 +40,7 @@ def output( ic )
44
40
  fail "DIN 16557 applies only to UN/EDIFACT data" unless ic.syntax=='E'
45
41
  ic.to_din16557_4.write($stdout,0)
46
42
  else
47
- ic.to_xml.write($stdout,0)
43
+ ic.to_xml.write($stdout)
48
44
  end
49
45
  end
50
46
 
@@ -54,12 +50,12 @@ def usage_and_exit
54
50
  Usage:
55
51
  #$0 [-D] [files ...]
56
52
  #$0 [-D] -z edifile.gz [files ...] (Zlib required)
57
- #$0 -s E|I [-D] (reads from stdin, E)difact or SAP I)doc)
53
+ #$0 -s A|E|I [-D] (reads from stdin, A)NSI X12, E)difact or SAP I)doc)
58
54
  Options:
59
55
  -d Activates debug mode by setting $DEBUG=true
60
56
  -D Generate DIN 16557-4 output.
61
- -s k Set standard key, k=E for UN/EDIFACT (default) or k=I for SAP IDOC
62
- (currently only E supported).
57
+ -s k Set standard key, k=E for UN/EDIFACT (default), k=A fir ANSI X12, or k=I for SAP IDOC
58
+ (currently only E and A supported).
63
59
  -z Require Zlib. You may then pass gzipped files directly.
64
60
  Note that Zlib is not always available.
65
61
  EOT
@@ -1,14 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
+ # -*- encoding: ISO-8859-1 -*-
2
3
  #
3
4
  # Tool to validate, list and analyze EDI data, based on EDI4R
4
5
  #
5
- # $Id: editool.rb,v 1.3 2006/08/01 11:19:41 werntges Exp $
6
- #
7
- # Author: Heinz W. Werntges (edi@informatik.fh-wiesbaden.de)
6
+ # Author: Heinz W. Werntges (edi@cs.hs-rm.de)
8
7
  #
9
8
  # License: This code is put under the Ruby license
10
9
  #
11
- # Copyright (c) 2006 Heinz W. Werntges, FH Wiesbaden
10
+ # Copyright (c) 2006, 2011 Heinz W. Werntges, RheinMain University of Applied Sciences, Wiesbaden
12
11
  #
13
12
 
14
13
  if $DEBUG # Include statement during test setup:
@@ -16,7 +15,7 @@ if $DEBUG # Include statement during test setup:
16
15
  require 'edi4r'
17
16
  require 'edi4r/edifact'
18
17
  require "edi4r/rexml"
19
- require_gem "edi4r-tdid"
18
+ require "edi4r-tdid"
20
19
  else # Regular include statements:
21
20
  require "rubygems"
22
21
  require "edi4r"
@@ -25,6 +24,7 @@ else # Regular include statements:
25
24
  rescue LoadError # Ignore error
26
25
  end
27
26
  require "edi4r/edifact"
27
+ require "edi4r/ansi_x12"
28
28
  require "edi4r/rexml"
29
29
  require "edi4r-tdid"
30
30
  end
@@ -63,6 +63,10 @@ def report_peek_result( ic_stub, fname )
63
63
  end
64
64
  puts "%15.15s\t%13s\t%13s\t%14s\t%s" % params
65
65
 
66
+ when 'A'
67
+ params = [fname, h.dI06, h.dI07, h.dI12, h.dI14=='T']
68
+ puts "%15.15s\t%13s\t%13s\t%14s\t%s" % params
69
+
66
70
  when 'I'
67
71
  puts "%15.15s\tSAP IDocs not supported yet" % [fname]
68
72
 
@@ -0,0 +1,1385 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Inter-Standard converter module SEDAS --> EANCOM'02
4
+ #
5
+ # Inhouse format: GS1 Germany's SEDAS V5 (April 1993)
6
+ # Output format: EANCOM'02 INVOIC, according to GS1 Germany'
7
+ # recommendations for application
8
+ # (EDI-Anwendungsempfehlungen V 2.0 (INVOIC) in EANCOM 2002 S3)
9
+ # and the general EANCOM 2002 (INVOIC) documentation
10
+ # Comments:
11
+ #
12
+ # Inhouse and output format were selected, because they represent typical
13
+ # data structures and tasks for users, and because documentation of
14
+ # these formats is freely available.
15
+ #
16
+ # $Id: sedas2eancom02.rb,v 1.3 2007/04/12 21:54:27 werntges Exp $
17
+ #
18
+ # Author: Heinz W. Werntges (edi@informatik.fh-wiesbaden.de)
19
+ #
20
+ # License: This code is put under the Ruby license
21
+ #
22
+ # Copyright (c) 2007 Heinz W. Werntges, FH Wiesbaden
23
+ #
24
+
25
+ # Include statement during test setup:
26
+ #if $DEBUG
27
+ # $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
28
+ #else
29
+ require 'rubygems'
30
+ #end
31
+
32
+ require 'edi4r'
33
+ require 'edi4r/edifact'
34
+ require 'edi4r/sedas'
35
+ require 'logger'
36
+
37
+
38
+ #
39
+ # Helper methods
40
+ #
41
+ # bbbbbbb 00000 c
42
+ # 1313131 31313 1
43
+
44
+ def bbn_to_gln( bbn )
45
+ bbn = "%07d" % bbn
46
+ raise "Keine bbn: #{bbn}" unless bbn =~ /\d{7}/
47
+ gln = bbn + '0' * (13-bbn.length)
48
+ sum = i = 0
49
+ gln.split('').reverse_each do |c|
50
+ sum += c.hex * ((i&1==1) ? 3 : 1)
51
+ i += 1
52
+ end
53
+ sum %= 10
54
+ return gln if sum==0
55
+ gln[-1] = ?9+1 - sum
56
+ gln
57
+ end
58
+
59
+ def to_value( komma_kz, field )
60
+ case komma_kz # Nr. 4
61
+ when 0: nil
62
+ when 1: field.to_i
63
+ when 2: field.to_i / 10.0
64
+ when 3: field.to_i / 100.0
65
+ when 4: field.to_i / 1000.0
66
+ when 5: field.to_i / 10000.0
67
+ else raise ArgumentError, "Unzulaessiges Komma-KZ: #{komma_kz}"
68
+ end
69
+ end
70
+
71
+ def qkey_to_qualifier6411( qkey ) # Nr. 5
72
+ case qkey
73
+ when nil: nil # not given - don't fill qualifier
74
+ when 0: nil # empty - should not occur, consider a raise()
75
+ when 11: nil # PCE
76
+ when 12: 'KGM'
77
+ when 13: 'LTR'
78
+ when 14: 'MTR'
79
+ when 15: nil # PCE (Anzahl Display/Sortimentseinheiten)
80
+ when 16: nil # PCE (Anzahl gesamt in den Display/Sortimentseinheiten)
81
+ # FIXME: 15, 16 nicht spezifiziert - Sonderbehandlung erforderlich?
82
+ else raise "QTY/PRI: Unbekannter 'Mengenschluessel: '#{qkey}'"
83
+ end
84
+ end
85
+
86
+ class EDI::E::Message
87
+ @@curr_year = Time.now.year.to_s # For add_dtm()
88
+
89
+ # d2380: Expect Fixnum or String, JMMTT or JJMMTT if d.2379.nil?
90
+ def add_dtm( data )
91
+ return nil if data[:d2380].nil? || data[:d2380] =~ /^\s*$/
92
+ dtm = self.new_segment("DTM")
93
+ dtm.cC507.d2005 = data[:d2005]
94
+ if data[:d2379].nil? # Auto-format
95
+ if data[:digits]==5 # Expected: 5 or 6
96
+ d = @@curr_year[0,3] + "%05d" % data[:d2380].to_i
97
+ else
98
+ d = @@curr_year[0,2] + "%06d" % (data[:d2380].to_i % 1000000)
99
+ end
100
+ dtm.cC507.d2380 = d
101
+ dtm.cC507.d2379 = 102
102
+ raise ArgumentError, "Datumsformat nicht 102!" if dtm.cC507.d2380.size != 8
103
+ else # User takes full control
104
+ dtm.cC507.d2380 = data[:d2380]
105
+ dtm.cC507.d2379 = data[:d2379]
106
+ end
107
+ self.add(dtm)
108
+ end
109
+
110
+ def add_nad( data )
111
+ return nil if (data[:d3039].nil? || data[:d3039] =~ /^\s*$/) && data[:d3036_1].nil?
112
+ seg = self.new_segment('NAD')
113
+ seg.d3035 = data[:d3035]
114
+ if data[:d3039].nil? || data[:d3039] =~ /^\s*$/
115
+ seg.cC080.a3036[0].value = data[:d3036_1].strip
116
+ seg.d3164 = data[:d3164].strip
117
+ seg.cC059.a3042[0].value = data[:d3042_1].strip
118
+ seg.d3251 = data[:d3251]
119
+ else
120
+ seg.cC082.d3039 = bbn_to_gln(data[:d3039])
121
+ seg.cC082.d3055 = 9
122
+ end
123
+ self.add seg
124
+ end
125
+
126
+ def add_rff( data )
127
+ return nil if data[:d1154].nil? || data[:d1154] =~ /^\s*$/
128
+ seg = self.new_segment('RFF')
129
+ seg.cC506.d1153 = data[:d1153]
130
+ if data[:d1154].is_a? Fixnum
131
+ seg.cC506.d1154 = data[:d1154]
132
+ else
133
+ seg.cC506.d1154 = data[:d1154].strip
134
+ end
135
+ self.add seg
136
+ end
137
+
138
+ def add_tax( data )
139
+ seg = self.new_segment("TAX")
140
+ return nil unless data[:ust_kz]
141
+ case kz=data[:ust_kz]
142
+ when 0: return nil # skip!
143
+ when 1,6: seg.cC243.d5278, seg.d5305 = 0, 'E' # 0%
144
+ when 2,7: seg.cC243.d5278, seg.d5305 = 7, 'S' # 7%
145
+ when 3: seg.cC243.d5278, seg.d5305 = 19, 'S' # 19%
146
+ when 8: seg.cC243.d5278, seg.d5305 = 16, 'S' # 16%
147
+ $log.warn("add_tax: USt-Kz 8! pos_nr=#{data[:pos_nr]}")
148
+ else raise "TAX: Unbekanntes Umsatzsteuer-Kennzeichen: '#{kz}' (pos_nr=#{data[:pos_nr]})"
149
+ end
150
+ seg.cC241.d5153= 'VAT'
151
+ seg.d5283 = 7
152
+ self.add seg
153
+ end
154
+
155
+ def add_pat( data )
156
+ seg = self.new_segment("PAT")
157
+ seg.d4279 = data[:d4279]
158
+ seg.cC112.d2475 = data[:d2475]
159
+ seg.cC112.d2009 = data[:d2009]
160
+ seg.cC112.d2151 = data[:d2151]
161
+ seg.cC112.d2152 = data[:d2152]
162
+ self.add seg
163
+ end
164
+
165
+ def add_alc( data )
166
+ seg = self.new_segment("ALC")
167
+ seg.d5463 = data[:d5463] || 'A'
168
+ seg.cC214.d7161 = data[:d7161] || 'DI'
169
+ seg.d1227 = data[:d1227]
170
+ self.add seg
171
+ end
172
+
173
+ def add_lin( data )
174
+ seg = self.new_segment("LIN")
175
+ return nil if data[:d7140].nil?
176
+ seg.d1082 = data[:d1082]
177
+ seg.cC212.d7140 = bbn_to_gln( data[:d7140] )
178
+ seg.cC212.d7143 = data[:d7143] || 'SRV'
179
+ if data[:c829_d1082]
180
+ seg.cC829.d5495 = data[:c829_d5495] || 1
181
+ seg.cC829.d1082 = data[:c829_d1082]
182
+ end
183
+ self.add seg
184
+ end
185
+
186
+ def add_pia( data )
187
+ seg = self.new_segment("PIA")
188
+ return nil if data[:d4347].nil?
189
+ seg.d4347 = data[:d4347] # e.g., 1
190
+ cde = seg.aC212[0]
191
+ cde.d7140 =
192
+ data[:d7140].respond_to?(:strip) ? data[:d7140].strip : data[:d7140]
193
+ cde.d7143 = data[:d7143]
194
+ cde.d3055 = data[:d3055]
195
+ self.add seg
196
+ end
197
+
198
+ def add_imd( data )
199
+ seg = self.new_segment("IMD")
200
+ return nil if data[:d7077].nil?
201
+ seg.d7077 = data[:d7077]
202
+ seg.cC272.d7081 = data[:d7081]
203
+ seg.cC272.d3055 = data[:d7081_3055]
204
+ seg.cC273.d7009 = data[:d7009]
205
+ seg.cC273.d3055 = data[:d7009_3055]
206
+ seg.cC273.a7008[0].value =
207
+ data[:d7008_1].respond_to?(:strip) ? data[:d7008_1].strip : data[:d7008_1]
208
+ self.add seg
209
+ end
210
+
211
+ def add_qty( data )
212
+ seg = self.new_segment("QTY")
213
+ return nil if data[:qkey]==0
214
+ seg.cC186.d6411 = qkey_to_qualifier6411( data[:qkey] )
215
+ seg.cC186.d6063 = data[:d6063]
216
+ seg.cC186.d6060 = data[:d6060]
217
+ self.add seg
218
+ end
219
+
220
+ def add_pri( data )
221
+ seg = self.new_segment("PRI")
222
+ case data[:pkey]
223
+ when nil: return nil # FIXME: Check! / Don't use this DE
224
+ when 0, 9: return nil # empty or w/o charge
225
+ when 1:
226
+ when 2: seg.cC509.d5284 = 100
227
+ when 3: seg.cC509.d5284 = 1000
228
+ else raise "PRI: Unbekannter 'Preisschluessel: '#{data[:pkey]}'"
229
+ end
230
+ seg.cC509.d5125 = data[:d5125]
231
+ seg.cC509.d6411 = qkey_to_qualifier6411( data[:qkey] )
232
+ seg.cC509.d5118 = data[:d5118]
233
+ self.add seg
234
+ end
235
+
236
+ def add_moa( data )
237
+ seg = self.new_segment("MOA")
238
+ seg.cC516.d5025 = data[:d5025]
239
+ seg.cC516.d5004 = data[:d5004]
240
+ self.add seg
241
+ end
242
+
243
+ def add_pcd( data )
244
+ seg = self.new_segment("PCD")
245
+ seg.cC501.d5245 = data[:d5245]
246
+ seg.cC501.d5482 = data[:d5482]
247
+ self.add seg
248
+ end
249
+
250
+ def add_rte( data )
251
+ seg = self.new_segment("RTE")
252
+ seg.cC128.d5419 = data[:d5419]
253
+ seg.cC128.d5420 = data[:d5420]
254
+ self.add seg
255
+ end
256
+
257
+ end
258
+
259
+
260
+ Verkettung01_Felder = Struct.new(:v_nad_by, :v_nad_ds,
261
+ :v_nad_su_va, :v_nad_su_fc, :v_nad_by_va,
262
+ :ls_a_kz, :ls_a_nr1, :ls_a_nr2)
263
+
264
+ Verkettung02_Felder = Struct.new(:empty, :ust_prozentsatz,
265
+ :preisschluessel, :komma_kz_p, :grundpreis,
266
+ :mengenschluessel, :komma_kz_m, :menge)
267
+
268
+
269
+ class SEDAS_to_EANCOM02_Map_INVOIC
270
+ include EDI
271
+
272
+ attr_accessor :with_ung
273
+
274
+
275
+ def map_00_to_unb( s00 )
276
+ unb = @ic.header
277
+ unb.cS002.d0004 = bbn_to_gln( s00.bbn_absender_dt )
278
+ unb.cS002.d0007 = 14
279
+ unb.cS003.d0010 = bbn_to_gln( s00.bbn_empfaenger_dt )
280
+ unb.cS003.d0007 = 14
281
+ unb.cS004.d0017 = s00.datum_erstellung
282
+ unb.cS004.d0019 = '0000'
283
+ unb.d0035 = 1 if s00.dateistatus =~ /[KT]/
284
+ unb.cS005.d0022 = s00.datei_archiv_nr.strip
285
+ unb.d0020 = s00.lfd_nr_dt_empfaenger
286
+ $ung_0048 = {} # Reset store for uniqueness check of UNG ref numbers
287
+ end
288
+
289
+
290
+ def map_01_to_ung( s01 )
291
+ grp = @ic.new_msggroup( @msg_params )
292
+ ung = grp.header
293
+ ung.cS006.d0040 = bbn_to_gln( s01.bbn_absender )
294
+ ung.cS006.d0007 = 14
295
+ ung.cS007.d0044 = bbn_to_gln( s01.bbn_empfaenger )
296
+ ung.cS007.d0007 = 14
297
+ ung.cS004.d0017 = s01.datum_erstellung
298
+ ung.cS004.d0019 = '0000'
299
+ log_nr = s01.log_nr
300
+ warn "SA01: Log-Nr. #{log_nr} bereits verwendet!" if $ung_0048[log_nr]
301
+ ung.d0048 = log_nr
302
+ $ung_0048[log_nr]=true
303
+ @ic.add grp
304
+ @p = grp
305
+ end
306
+
307
+
308
+ def map_12_to_325( s_msg, master_data )
309
+ @msg = @p.new_message( @msg_params )
310
+ item_counter = 1
311
+
312
+ # Lookahead / out-of-sequence segments first:
313
+ #
314
+ ftx_in_header = []
315
+ # 08 for SA 12/22, 15/25
316
+ s_msg[/[12][25]08/].each do |seg|
317
+ ftx = @msg.new_segment("FTX")
318
+ ftx.d4451 = 'ZZZ'
319
+ ftx.cC108.a4440[0].value = seg.freitext.strip
320
+ ftx.d3453 = 'DE'
321
+ ftx_in_header << ftx
322
+ end
323
+ # 08 for SA 14/24, 17/27
324
+ s_msg[/[12][47]08/].each do |seg|
325
+ ftx = @msg.new_segment("FTX")
326
+ ftx.d4451 = 'SUR'
327
+ ftx.cC108.a4440[0].value = seg.freitext.strip
328
+ ftx.d3453 = 'DE'
329
+ ftx_in_header << ftx
330
+ end
331
+
332
+ s01 = Verkettung01_Felder.new
333
+ if seg=s_msg[/[12][25]01/].first # 1201, 2201, 1501, 2501
334
+ s01.v_nad_by = seg.bbn_rechnungsempfaenger
335
+ s01.v_nad_ds = seg.bbn_warenlieferant
336
+ s01.v_nad_su_va = seg.ust_ident_nr_lieferant
337
+ s01.v_nad_su_fc = seg.steuer_nr
338
+ s01.v_nad_by_va = seg.ust_ident_nr_erwerber
339
+ s01.ls_a_kz = seg.ls_auftrag_kz
340
+ s01.ls_a_nr1 = seg.ls_auftrag_nr_1
341
+ s01.ls_a_nr2 = seg.ls_auftrag_nr_2
342
+ else
343
+ s01.ls_a_kz = 0
344
+ end
345
+
346
+ # Naming convention:
347
+ #
348
+ # shd = SEDAS header record, e.g. a SA12/22 or a SA15/25
349
+ # str = SEDAS trailer record, e.g. a SA14/24 or a SA17/27
350
+ #
351
+ shd = s_msg[/[12][25]00/].first # Must exist due to SEDAS msg definition!
352
+ all_str = s_msg[/[12][47]00/] # Expect one per VAT rate, one at least
353
+
354
+ ls_auftrag_array = [[shd.ls_auftrag_kz_1, shd.ls_auftrag_nr_1, 1],
355
+ [shd.ls_auftrag_kz_2, shd.ls_auftrag_nr_2, 2],
356
+ [shd.ls_auftrag_kz_3, shd.ls_auftrag_nr_3, 3],
357
+ [shd.ls_auftrag_kz_4, shd.ls_auftrag_nr_4, 4],
358
+ [shd.ls_auftrag_kz_5, shd.ls_auftrag_nr_5, 5]]
359
+
360
+ summe_131 = 0 # Evtl. erst Skonto addieren, siehe ALC (EAB)
361
+
362
+ # BGM
363
+ #
364
+ bgm = @msg.new_segment("BGM")
365
+ bgm.cC002.d1001 = 325
366
+ bgm.cC106.d1004 = shd.beleg_nr
367
+ bgm.d1225 = 9
368
+ @msg.add(bgm)
369
+
370
+ # All DTM
371
+ #
372
+ @msg.add_dtm(:d2005=> 137, :digits=> 5, :d2380=>shd.datum_liefernachweis)
373
+ @msg.add_dtm(:d2005=> 35, :d2380=> shd.lieferdatum)
374
+ [
375
+ [shd.ls_auftrag_kz, shd.ls_auftrag_nr, shd.auftrags_nr_besteller, 1],
376
+ [s01.ls_a_kz, s01.ls_a_nr1, s01.ls_a_nr2, 2]
377
+ ].each do |a|
378
+ kz, nr1, nr2, no = a
379
+ next unless kz==2 # Lieferdatum von-bis (0JJMMTT)
380
+ dt = if nr1.is_a? String
381
+ @curr_year[0,2]+nr1[1,6]+@curr_year[0,2]+nr2[1,6]
382
+ else
383
+ @curr_year[0,2]+"%06d" % nr1 + @curr_year[0,2]+"%06d" % nr2
384
+ end
385
+ @msg.add_dtm( :d2005=>35, :d2380=>dt, :d2379=>718 ) # CCYYMMDD-CCYYMMDD
386
+ end
387
+
388
+ # All FTX
389
+ #
390
+ ftx_in_header.each {|obj| @msg.add(obj)}
391
+
392
+ # SG1: RFF-DTM
393
+ #
394
+ case q=shd.s_rechnung_kz # Nr. 2, Anmerkung 2
395
+ when 0 # empty - skip
396
+ when 6,8
397
+ @msg.add_rff( :d1153=>'IV', :d1154=> shd.s_rechnung_nr )
398
+ @msg.add_dtm( :d2005=> 171, :d2380=> shd.datum_s_beleg )
399
+ when 7,9
400
+ @msg.add_rff( :d1153=>'IV', :d1154=> shd.s_rechnung_nr )
401
+ else
402
+ raise "Kennzeichen S.-Rechnung: ungueltig: #{q}"
403
+ end
404
+
405
+ [
406
+ [shd.ls_auftrag_kz, shd.ls_auftrag_nr, shd.auftrags_nr_besteller, 1],
407
+ [s01.ls_a_kz, s01.ls_a_nr1, s01.ls_a_nr2, 2]
408
+ ].each do |a|
409
+ kz, nr1, nr2, no = a
410
+ case kz # Nr. 3, Anmerkung 1
411
+ when nil # treat as empty, should not occur
412
+ $log.warn("ls_auftrag_kz (#{no}) fehlt!")
413
+ when 0 # empty - skip
414
+ when 1
415
+ @msg.add_rff( :d1153=>'DQ', :d1154=> nr1 )
416
+ @msg.add_rff( :d1153=>'ON', :d1154=> nr2 )
417
+ when 2 # skip - treated earlier (DTM)
418
+ when 3
419
+ @msg.add_rff( :d1153=>'ON', :d1154=> nr1 )
420
+ when 4
421
+ @msg.add_rff( :d1153=>'DQ', :d1154=> nr1 )
422
+ else
423
+ raise "Kennzeichen LS/Auftrag (#{no}) ungueltig (Anm. 1): '#{kz}'"
424
+ end
425
+ end
426
+
427
+ ls_auftrag_array.each do |a|
428
+ kz, nr1, no = a
429
+ case kz # Nr. 3, Anmerkung 3
430
+ when 0 # empty - skip
431
+ when 5 # Lieferscheinnummer
432
+ @msg.add_rff( :d1153=>'DQ', :d1154=> nr1 )
433
+ when 6 # Auftragsnummer
434
+ @msg.add_rff( :d1153=>'ON', :d1154=> nr1 )
435
+ else
436
+ raise "Kennzeichen LS/Auftrag (#{no}) ungueltig (Anm. 3): '#{kz}'"
437
+ end
438
+ end # each
439
+
440
+ # SG2: NAD-RFF
441
+ #
442
+ @msg.add_nad( :d3035=>'SU', :d3039=> shd.bbn_lieferant )
443
+ @msg.add_rff( :d1153=>'VA', :d1154=> s01.v_nad_su_va )
444
+ @msg.add_rff( :d1153=>'FC', :d1154=> s01.v_nad_su_fc )
445
+
446
+ bbn = s01.v_nad_by
447
+ bbn = shd.bbn_rechnungsempfaenger if bbn.nil? || bbn == 0
448
+ @msg.add_nad( :d3035=>'BY', :d3039=> bbn )
449
+ @msg.add_rff( :d1153=>'VA', :d1154=> s01.v_nad_by_va )
450
+
451
+ case kz=shd.warenempfaenger_kz
452
+ when 1
453
+ bbn = shd.bbn_warenempfaenger.to_s + "%06d" % shd.interne_nr
454
+ @msg.add_nad( :d3035=>'DP', :d3039=> bbn )
455
+ when 2
456
+ s51 = master_data['51'].find{|s| s.refpos_nr == shd.positions_nr}
457
+ raise "Sorry - SA51 fehlt fuer pos_nr={shd.positions_nr}" unless s51
458
+ @msg.add_nad( :d3035=>'DP', :d3036_1=> s51.name_warenempfaenger,
459
+ :d3164=> s51.ort,
460
+ :d3042_1=> s51.strasse_postfach,
461
+ :d3251=> s51.plz_1==0 ? s51.plz_2.strip : s51.plz_1 )
462
+ # @msg.add_rff( :d1153=>'IA', :d1154=> shd.interne_nr )
463
+ when 3
464
+ bbn = shd.bbn_warenempfaenger
465
+ @msg.add_nad( :d3035=>'DP', :d3039=> bbn )
466
+ else
467
+ raise "Warenempfaenger-bbs: 'Kennzeichen' ungueltig: '#{kz}'"
468
+ end
469
+
470
+ # TAX-MOA SG6
471
+ #
472
+ all_str.each {|s| @msg.add_tax( :ust_kz=> s.ust_kz, :pos_nr=> s.positions_nr ) }
473
+
474
+ # ALC-MOA SG16
475
+ #
476
+ all_str.each do |s|
477
+ kz, betrag = s.fracht_verpackung_kz, s.fracht_verpackung
478
+ next if kz == 0
479
+ params = {}
480
+ params[:d5463] = betrag > 0 ? 'C' : 'A'
481
+ params[:d7161] = case kz
482
+ when 1: 'FC'
483
+ when 2: 'PC'
484
+ when 3: 'IN'
485
+ when 4,5,6,7: 'SH'
486
+ else raise "'Fracht/Verpackungs-Kennzeichen' ungueltig: '#{kz}'"
487
+ end
488
+ @msg.add_alc( params )
489
+ @msg.add_moa( :d5025=> 8, :d5004=> betrag.abs/100.0 )
490
+ end
491
+
492
+ all_str.each do |s|
493
+ betrag = s.skonto
494
+ next if betrag == 0
495
+ @msg.add_alc( :d5463=> 'A', :d1227=> 1, :d7161=> 'EAB' )
496
+ @msg.add_moa( :d5025=> 8, :d5004=> betrag.abs/100.0 )
497
+ summe_131 += betrag
498
+ warn "Skonto positiv: #{s.skonto}, pos_nr=#{s.positions_nr}" if betrag>0
499
+ end
500
+
501
+ all_str.each do |s| # Also see code at item level, SG39
502
+ [[s.rabatt_kz_1, s.nachweisrabatt_1, 1],
503
+ [s.rabatt_kz_2, s.nachweisrabatt_2, 2],
504
+ [s.rabatt_kz_3, s.nachweisrabatt_3, 3],
505
+ [s.rabatt_kz_4, s.nachweisrabatt_4, 4]].each do |a|
506
+ kz, betrag, no = a
507
+ next if kz == 0
508
+
509
+ params = {
510
+ :d5463=> betrag < 0 ? 'A' : 'C',
511
+ :d7161=> 'DI', :d1227=> kz%10 - 1
512
+ }
513
+ params[:d1227] = nil unless params[:d1227].between?(1,4)
514
+ @msg.add_alc( params )
515
+
516
+ case (kz - kz%10)/10
517
+ when 1..4, 6
518
+ @msg.add_moa( :d5025=> 8, :d5004=> betrag.abs/100.0 )
519
+ when 5
520
+ @msg.add_moa( :d5025=> 8, :d5004=> betrag.abs/100.0 )
521
+ pri_aab.d5125 = 'AAA' if pri_aab # Why?
522
+ when 7
523
+ @msg.add_pcd( :d5245=> 3, :d5482=> betrag.abs/10000.0 )
524
+ when 8
525
+ @msg.add_rte( :d5419=> 1, :d5420=> betrag.abs/10000.0 )
526
+ else
527
+ raise "ALC: Unbekanntes 'Rabatt-Kennzeichen: '#{kz}'"
528
+ end
529
+ end
530
+ end
531
+
532
+ # Items: Map elsewhere
533
+ #
534
+ s_msg[/[12][36]00/].each do |s|
535
+ map_12_to_325_item( s.descendants_and_self, item_counter, master_data )
536
+ item_counter += 2
537
+ end
538
+
539
+ # Trailer
540
+ #
541
+ uns = @msg.new_segment("UNS")
542
+ uns.d0081 = 'S'
543
+ @msg.add(uns)
544
+
545
+ # MOA SG50
546
+ #
547
+ summe_402, summe_79, summe_77 = 0,0.0, 0
548
+ # summe_131: siehe oben
549
+ all_str.each do |s|
550
+ if s.b_vereinbarung_kz_1 == 2 # Nr. 24
551
+ summe_402 += s.b_vereinbarung_1
552
+ end
553
+ summe_131 += s.nachweisrabatt_gesamt
554
+ summe_77 += s.nachweis_endbetrag
555
+ if s.warenwert_kz == 1 # Nr. 11: 1=Warenwert
556
+ summe_79 += to_value( s.komma_kz, s.warenbetrag_menge )
557
+ end
558
+ end
559
+ @msg.add_moa( :d5025=> 402, :d5004=> summe_402/100.0 ) if summe_402 != 0
560
+ @msg.add_moa( :d5025=> 131, :d5004=> summe_131/100.0 ) if summe_131 != 0
561
+ @msg.add_moa( :d5025=> 79, :d5004=> summe_79 )
562
+ @msg.add_moa( :d5025=> 77, :d5004=> summe_77 /100.0 ) if summe_77 != 0
563
+
564
+ # TAX-MOA SG52, only if more than 1 STR record found
565
+ #
566
+ all_str.each do |s|
567
+ @msg.add_tax( :ust_kz=> s.ust_kz, :pos_nr=> s.positions_nr )
568
+ if s.warenwert_kz == 1 # Nr. 11: 1=Warenwert
569
+ @msg.add_moa( :d5025=> 79, :d5004=> s.warenbetrag_menge/100.0 )
570
+ end
571
+ betrag = s.nachweisrabatt_gesamt + s.skonto
572
+ @msg.add_moa( :d5025=> 131, :d5004=> betrag/100.0 ) if betrag != 0
573
+ end if all_str.size > 1
574
+
575
+ @p.add @msg, false
576
+ end
577
+
578
+
579
+ def map_12_to_325_item( segments, item_counter, master_data )
580
+ # Lookahead / out-of-sequence segments first:
581
+ ftx_in_item = []
582
+ segments.find_all{|seg| seg.name=~/[12][36]08/}.each do |seg|
583
+ ftx = @msg.new_segment("FTX")
584
+ ftx.d4451 = 'ZZZ'
585
+ ftx.cC108.a4440[0].value = seg.freitext.strip
586
+ ftx.d3453 = 'DE'
587
+ ftx_in_item << ftx
588
+ end
589
+
590
+ s02 = Verkettung02_Felder.new
591
+ if seg=segments.find{|s| s.name=~/[12][36]02/} # just 0..1 expected
592
+ s02.ust_prozentsatz = seg.ust_prozentsatz
593
+ s02.preisschluessel = seg.preisschluessel
594
+ s02.komma_kz_p = seg.komma_kz_p
595
+ s02.grundpreis = seg.grundpreis
596
+ s02.mengenschluessel = seg.mengenschluessel
597
+ s02.komma_kz_m = seg.komma_kz_m
598
+ s02.menge = seg.menge
599
+ s02.empty = false
600
+ else
601
+ s02.empty = true
602
+ end
603
+
604
+ # Now do the serial mapping
605
+ segments.each do |seg|
606
+
607
+ seg_id = seg.name
608
+ seg_id += ' ' + seg.sg_name if seg.sg_name
609
+ case seg_id
610
+ when /[12][36]00 SG1/
611
+ # FIXME: Linear search - consider a hash for fast lookup via ean.
612
+ s5000 = master_data.find{|s| s.name=='5000' && s.ean==seg.ean}
613
+ raise "SA 50 fehlt fuer EAN #{seg.ean}, pos_nr=#{seg.positions_nr}" unless s5000
614
+
615
+ @msg.add_lin( :d1082=> item_counter, :d7140=> seg.ean)
616
+
617
+ # PIA
618
+ #
619
+ @msg.add_pia( :d4347=> 1, :d7140=> s5000.artikel_nr, :d7143=> 'SA')
620
+
621
+ # IMD
622
+ #
623
+ @msg.add_imd( :d7077=> 'C', :d7009=> 'IN', :d7009_3055=> 9)
624
+ @msg.add_imd( :d7077=> 'A', :d7008_1=> s5000.langtext)
625
+
626
+ # QTY
627
+ #
628
+ if s02.empty || s02.mengenschluessel == 0 || s02.komma_kz_m == 0
629
+ mengenschluessel, komma_kz_m, menge =
630
+ seg.mengenschluessel, seg.komma_kz_m, seg.menge
631
+ else
632
+ mengenschluessel, komma_kz_m, menge =
633
+ s02.mengenschluessel, s02.komma_kz_m, s02.menge
634
+ end
635
+ if s02.empty || s02.preisschluessel == 0 || s02.komma_kz_p == 0
636
+ preisschluessel, komma_kz_p, grundpreis =
637
+ seg.preisschluessel, seg.komma_kz_p, seg.grundpreis
638
+ else
639
+ preisschluessel, komma_kz_p, grundpreis =
640
+ s02.preisschluessel, s02.komma_kz_p, s02.grundpreis
641
+ end
642
+ @msg.add_qty( :d6063=> preisschluessel == 9 ? 192 : 47, # w/o charge?
643
+ :d6060=> to_value( komma_kz_m, menge ),
644
+ :qkey=> mengenschluessel )
645
+
646
+ # All FTX
647
+ #
648
+ ftx_in_item.each {|obj| @msg.add(obj)}
649
+
650
+ # MOA
651
+ #
652
+ betrag = seg.artikelrabatt_gesamt
653
+ @msg.add_moa( :d5025=> 131, :d5004=> betrag/100.0 ) if betrag != 0
654
+ betrag = seg.warenwert
655
+ @msg.add_moa( :d5025=> 203, :d5004=> betrag/100.0 ) if betrag != 0
656
+ if seg.b_vereinbarung_kz_2 == 3
657
+ @msg.add_moa( :d5025=> 204, :d5004=> seg.b_vereinbarung_2/10.0 )
658
+ end
659
+
660
+ # PRI
661
+ #
662
+ pri_aab =
663
+ @msg.add_pri(:d5125=>'AAB', :d5118=>to_value( komma_kz_p,grundpreis),
664
+ :pkey=>preisschluessel, :qkey=>mengenschluessel )
665
+ if seg.schluessel_kz == 1
666
+ @msg.add_pri( :d5125=>'AAE',
667
+ :d5118=>to_value( seg.komma_kz, seg.sondereintrag ) )
668
+ # :pkey=>preisschluessel, :qkey=>mengenschluessel)
669
+ elsif seg.schluessel_kz == 3
670
+ @msg.add_pri( :d5125=>'AAA',
671
+ :d5118=>to_value( seg.komma_kz, seg.sondereintrag ) )
672
+ end
673
+
674
+ # RFF-DTM SG30
675
+ #
676
+ if seg.b_vereinbarung_kz_2 == 5
677
+ @msg.add_rff( :d1153=>'AAK', :d1154=> seg.b_vereinbarung_2 )
678
+ end
679
+
680
+ # TAX-MOA
681
+ #
682
+ @msg.add_tax( :ust_kz=> seg.umsatzsteuer_kz, :pos_nr=> seg.positions_nr )
683
+
684
+
685
+ # ALC SG39
686
+ #
687
+ [[seg.rabatt_kz_1, seg.artikelrabatt_1, 1],
688
+ [seg.rabatt_kz_2, seg.artikelrabatt_2, 2],
689
+ [seg.rabatt_kz_3, seg.artikelrabatt_3, 3],
690
+ [seg.rabatt_kz_4, seg.artikelrabatt_4, 4]].each do |a|
691
+ kz, betrag, no = a
692
+ next if kz == 0
693
+
694
+ params = {
695
+ :d5463=> betrag < 0 ? 'A' : 'C',
696
+ :d7161=> 'DI', :d1227=> kz%10 - 1
697
+ }
698
+ params[:d1227] = nil unless params[:d1227].between?(1,4)
699
+ @msg.add_alc( params )
700
+
701
+ case (kz - kz%10)/10
702
+ when 1..4, 6
703
+ @msg.add_moa( :d5025=> 8, :d5004=> betrag.abs/100.0 )
704
+ when 5
705
+ @msg.add_moa( :d5025=> 8, :d5004=> betrag.abs/100.0 )
706
+ pri_aab.d5125 = 'AAA' if pri_aab # Why?
707
+ when 7
708
+ @msg.add_pcd( :d5245=> 3, :d5482=> betrag.abs/10000.0 )
709
+ when 8
710
+ @msg.add_rte( :d5419=> 1, :d5420=> betrag.abs/10000.0 )
711
+ else
712
+ raise "ALC: Unbekanntes 'Rabatt-Kennzeichen: '#{kz}'"
713
+ end
714
+ end
715
+
716
+ # Sub-line info
717
+ #
718
+ @msg.add_lin( :d1082=> item_counter+1,
719
+ :d7140=> s5000.kleinste_ean,
720
+ :c829_d1082 => item_counter )
721
+
722
+ @msg.add_pia( :d4347=> 1, :d7140=> "%04d" % s5000.klassifikation,
723
+ :d7143=> 'GN')
724
+ @msg.add_imd( :d7077=> 'C', :d7009=> 'CU', :d7009_3055=> 9)
725
+ @msg.add_qty( :d6063=> 59, :d6060=> s5000.menge_ean )
726
+
727
+
728
+ when /[12][36]0[28] SG1/
729
+ # ignored here - handled by lookahead section
730
+
731
+ else
732
+ raise "Segment-Id #{seg_id} hier nicht erwartet!"
733
+ end
734
+ end
735
+ end
736
+
737
+
738
+ def map_15_to_38x( s_msg, master_data )
739
+ @msg = @p.new_message( @msg_params )
740
+ item_counter = 1
741
+
742
+ # Lookahead / out-of-sequence segments first:
743
+ #
744
+ ftx_in_header = []
745
+ # 08 for SA 12/22, 15/25
746
+ s_msg[/[12][25]08/].each do |seg|
747
+ ftx = @msg.new_segment("FTX")
748
+ ftx.d4451 = 'ZZZ'
749
+ ftx.cC108.a4440[0].value = seg.freitext.strip
750
+ ftx.d3453 = 'DE'
751
+ ftx_in_header << ftx
752
+ end
753
+ # 08 for SA 14/24, 17/27
754
+ s_msg[/[12][47]08/].each do |seg|
755
+ ftx = @msg.new_segment("FTX")
756
+ ftx.d4451 = 'SUR'
757
+ ftx.cC108.a4440[0].value = seg.freitext.strip
758
+ ftx.d3453 = 'DE'
759
+ ftx_in_header << ftx
760
+ end
761
+
762
+ s01 = Verkettung01_Felder.new
763
+ if seg=s_msg[/[12][25]01/].first # 1201, 2201, 1501, 2501
764
+ s01.v_nad_by = seg.bbn_rechnungsempfaenger
765
+ s01.v_nad_ds = seg.bbn_warenlieferant
766
+ s01.v_nad_su_va = seg.ust_ident_nr_lieferant
767
+ s01.v_nad_su_fc = seg.steuer_nr
768
+ s01.v_nad_by_va = seg.ust_ident_nr_erwerber
769
+ s01.ls_a_kz = seg.ls_auftrag_kz
770
+ s01.ls_a_nr1 = seg.ls_auftrag_nr_1
771
+ s01.ls_a_nr2 = seg.ls_auftrag_nr_2
772
+ else
773
+ s01.ls_a_kz = 0
774
+ end
775
+
776
+ # Naming convention:
777
+ #
778
+ # shd = SEDAS header record, e.g. a SA12/22 or a SA15/25
779
+ # str = SEDAS trailer record, e.g. a SA14/24 or a SA17/27
780
+ #
781
+ shd = s_msg[/[12][25]00/].first # Must exist due to SEDAS msg definition!
782
+ all_str = s_msg[/[12][47]00/] # Expect one per VAT rate, one at least
783
+
784
+ ls_auftrag_array = [[shd.ls_auftrag_kz_1, shd.ls_auftrag_nr_1, 1],
785
+ [shd.ls_auftrag_kz_2, shd.ls_auftrag_nr_2, 2],
786
+ [shd.ls_auftrag_kz_3, shd.ls_auftrag_nr_3, 3],
787
+ [shd.ls_auftrag_kz_4, shd.ls_auftrag_nr_4, 4],
788
+ [shd.ls_auftrag_kz_5, shd.ls_auftrag_nr_5, 5]]
789
+
790
+ summe_77 = all_str.inject(0){|sum, s| sum + s.endbetrag}
791
+ summe_131 = 0 # Evtl. erst Skonto addieren, siehe ALC (EAB)
792
+ vz = summe_77<=>0 # Vorzeichen, Unterscheidung Rechnung(+1)/Gutschrift(-1)
793
+
794
+ # BGM
795
+ #
796
+ bgm = @msg.new_segment("BGM")
797
+ bgm.cC002.d1001 = (vz < 0) ? 381 : 380
798
+ bgm.cC106.d1004 = shd.beleg_nr
799
+ bgm.d1225 = 9
800
+ @msg.add(bgm)
801
+
802
+ # All DTM
803
+ #
804
+ @msg.add_dtm(:d2005=> 137, :digits=> 5, :d2380=>shd.datum_rechnung)
805
+ if shd.lieferdatum==0
806
+ warn "Re-Nr #{shd.beleg_nr}, Pos-Nr #{shd.positions_nr}: " +
807
+ "Lieferdatum fehlt - verwende Belegdatum!"
808
+ @msg.add_dtm(:d2005=> 35, :d2380=> shd.datum_rechnung)
809
+ else
810
+ @msg.add_dtm(:d2005=> 35, :d2380=> shd.lieferdatum)
811
+ end
812
+
813
+ [
814
+ [shd.ls_auftrag_kz, shd.ls_auftrag_nr, shd.auftrags_nr_besteller, 1],
815
+ [s01.ls_a_kz, s01.ls_a_nr1, s01.ls_a_nr2, 2]
816
+ ].each do |a|
817
+ kz, nr1, nr2, no = a
818
+ next unless kz==2 # Lieferdatum von-bis (0JJMMTT)
819
+ dt = if nr1.is_a? String
820
+ @curr_year[0,2]+nr1[1,6]+@curr_year[0,2]+nr2[1,6]
821
+ else
822
+ @curr_year[0,2]+"%06d" % nr1 + @curr_year[0,2]+"%06d" % nr2
823
+ end
824
+ @msg.add_dtm( :d2005=>35, :d2380=>dt, :d2379=>718 ) # CCYYMMDD-CCYYMMDD
825
+ end
826
+
827
+ # ALI
828
+ #
829
+ # Anmerkung 18:
830
+ if shd.b_vereinbarung_kz_1==6 || shd.b_vereinbarung_kz_2==6
831
+ ali = @msg.new_segment("ALI")
832
+ bgm.a4183[0].value = 15
833
+ @msg.add ali
834
+ end
835
+ # FIXME: Trap case "b_vereinbarung_kz_x != 1 or 6
836
+
837
+ # All FTX
838
+ #
839
+ ftx_in_header.each {|obj| @msg.add(obj)}
840
+
841
+ # SG1: RFF-DTM
842
+ #
843
+ case q=shd.reli_zahlungsempfaenger_kz # Nr. 2, Anmerkung 17
844
+ when 0 # empty - skip
845
+ when 1
846
+ @msg.add_rff( :d1153=>'ABO', :d1154=> shd.rz_eintrag_1 )
847
+ @msg.add_dtm( :d2005=> 171, :d2380=> shd.rz_eintrag_2 )
848
+ when 2
849
+ @msg.add_rff( :d1153=>'ABO', :d1154=> shd.rz_eintrag_1 )
850
+ when 6
851
+ @msg.add_rff( :d1153=>'IV', :d1154=> shd.rz_eintrag_1 )
852
+ @msg.add_dtm( :d2005=> 171, :d2380=> shd.rz_eintrag_2 )
853
+ when 7
854
+ @msg.add_rff( :d1153=>'IV', :d1154=> shd.rz_eintrag_1 )
855
+ else
856
+ raise "S.-Rechnung: 'Kennzeichen' ungueltig: #{q}"
857
+ end
858
+
859
+ [
860
+ [shd.ls_auftrag_kz, shd.ls_auftrag_nr, shd.auftrags_nr_besteller, 1],
861
+ [s01.ls_a_kz, s01.ls_a_nr1, s01.ls_a_nr2, 2]
862
+ ].each do |a|
863
+ kz, nr1, nr2, no = a
864
+ case kz # Nr. 3, Anmerkung 1
865
+ when nil # treat as empty, should not occur
866
+ $log.warn("ls_auftrag_kz (#{no}) fehlt!")
867
+ when 0 # empty - skip
868
+ when 1
869
+ @msg.add_rff( :d1153=>'DQ', :d1154=> nr1 )
870
+ @msg.add_rff( :d1153=>'ON', :d1154=> nr2 )
871
+ when 2 # skip - treated earlier (DTM)
872
+ when 3
873
+ @msg.add_rff( :d1153=>'ON', :d1154=> nr1 )
874
+ when 4
875
+ @msg.add_rff( :d1153=>'DQ', :d1154=> nr1 )
876
+ else
877
+ raise "Kennzeichen LS/Auftrag (#{no}) ungueltig (Anm. 1): '#{kz}'"
878
+ end
879
+ end
880
+
881
+ ls_auftrag_array.each do |a|
882
+ kz, nr1, no = a
883
+ case kz # Nr. 3, Anmerkung 3
884
+ when 0 # empty - skip
885
+ when 5 # Lieferscheinnummer
886
+ @msg.add_rff( :d1153=>'DQ', :d1154=> nr1 )
887
+ when 6 # Auftragsnummer
888
+ @msg.add_rff( :d1153=>'ON', :d1154=> nr1 )
889
+ else
890
+ raise "Kennzeichen LS/Auftrag (#{no}) ungueltig (Anm. 3): '#{kz}'"
891
+ end
892
+ end # each
893
+
894
+ # SG2: NAD-RFF
895
+ #
896
+ @msg.add_nad( :d3035=>'SU', :d3039=> shd.bbn_lieferant )
897
+ @msg.add_rff( :d1153=>'VA', :d1154=> s01.v_nad_su_va )
898
+ @msg.add_rff( :d1153=>'FC', :d1154=> s01.v_nad_su_fc )
899
+
900
+ bbn = s01.v_nad_by
901
+ bbn = shd.bbn_rechnungsempfaenger if bbn.nil? || bbn == 0
902
+ @msg.add_nad( :d3035=>'BY', :d3039=> bbn )
903
+ @msg.add_rff( :d1153=>'VA', :d1154=> s01.v_nad_by_va )
904
+ # Anmerkung 18:
905
+ @msg.add_rff( :d1153=>'YC1',:d1154=> shd.b_vereinbarung_1 ) if
906
+ shd.b_vereinbarung_kz_1 == 1
907
+ @msg.add_rff( :d1153=>'YC1',:d1154=> shd.b_vereinbarung_2 ) if
908
+ shd.b_vereinbarung_kz_2 == 1
909
+
910
+ case kz=shd.warenempfaenger_kz
911
+ when 1
912
+ bbn = shd.bbn_warenempfaenger.to_s + "%06d" % shd.interne_nr
913
+ @msg.add_nad( :d3035=>'DP', :d3039=> bbn )
914
+ when 2
915
+ s51 = master_data['51'].find{|s| s.refpos_nr == shd.positions_nr}
916
+ raise "Sorry - SA51 fehlt fuer pos_nr={shd.positions_nr}" unless s51
917
+ @msg.add_nad( :d3035=>'DP', :d3036_1=> s51.name_warenempfaenger,
918
+ :d3164=> s51.ort,
919
+ :d3042_1=> s51.strasse_postfach,
920
+ :d3251=> s51.plz_1==0 ? s51.plz_2.strip : s51.plz_1 )
921
+ # @msg.add_rff( :d1153=>'IA', :d1154=> shd.interne_nr )
922
+ when 3
923
+ bbn = shd.bbn_warenempfaenger
924
+ @msg.add_nad( :d3035=>'DP', :d3039=> bbn )
925
+ else
926
+ raise "Warenempfaenger-bbs: 'Kennzeichen' ungueltig: '#{kz}'"
927
+ end
928
+
929
+ # TAX-MOA SG6
930
+ #
931
+ all_str.each {|s| @msg.add_tax( :ust_kz=> s.ust_kz, :pos_nr=> s.positions_nr ) }
932
+
933
+ # ALC-MOA SG16
934
+ #
935
+ all_str.each do |s|
936
+ kz, betrag = s.fracht_verpackung_kz, s.frachtbelastung
937
+ next if kz == 0
938
+ params = {}
939
+ params[:d5463] = betrag > 0 ? 'C' : 'A'
940
+ params[:d7161] = case kz
941
+ when 1: 'FC'
942
+ when 2: 'PC'
943
+ when 3: 'IN'
944
+ when 4,5,6,7: 'SH'
945
+ else raise "'Fracht/Verpackungs-Kennzeichen' ungueltig: '#{kz}'"
946
+ end
947
+ @msg.add_alc( params )
948
+ @msg.add_moa( :d5025=> 8, :d5004=> betrag.abs/100.0 )
949
+ end
950
+
951
+ all_str.each do |s|
952
+ betrag = s.skonto
953
+ next if betrag == 0
954
+ @msg.add_alc( :d5463=> 'A', :d1227=> 1, :d7161=> 'EAB' )
955
+ @msg.add_moa( :d5025=> 8, :d5004=> betrag.abs/100.0 )
956
+ summe_131 += betrag
957
+ warn "Skonto positiv: #{betrag}, pos_nr=#{s.positions_nr}" if vz*betrag>0
958
+ end
959
+
960
+ all_str.each do |s| # Also see code at item level, SG39
961
+ [[s.rabatt_kz_1, s.rechnungsrabatt_1, 1],
962
+ [s.rabatt_kz_2, s.rechnungsrabatt_2, 2],
963
+ [s.rabatt_kz_3, s.rechnungsrabatt_3, 3],
964
+ [s.rabatt_kz_4, s.rechnungsrabatt_4, 4]].each do |a|
965
+ kz, betrag, no = a
966
+ next if kz == 0
967
+
968
+ params = {
969
+ :d5463=> betrag < 0 ? 'A' : 'C',
970
+ :d7161=> 'DI', :d1227=> kz%10 - 1
971
+ }
972
+ params[:d1227] = nil unless params[:d1227].between?(1,4)
973
+ @msg.add_alc( params )
974
+
975
+ case (kz - kz%10)/10
976
+ when 1..4, 6
977
+ @msg.add_moa( :d5025=> 8, :d5004=> betrag.abs/100.0 )
978
+ when 5
979
+ @msg.add_moa( :d5025=> 8, :d5004=> betrag.abs/100.0 )
980
+ pri_aab.d5125 = 'AAA' if pri_aab # Why?
981
+ when 7
982
+ @msg.add_pcd( :d5245=> 3, :d5482=> betrag.abs/10000.0 )
983
+ when 8
984
+ @msg.add_rte( :d5419=> 1, :d5420=> betrag.abs/10000.0 )
985
+ else
986
+ raise "ALC: Unbekanntes 'Rabatt-Kennzeichen: '#{kz}'"
987
+ end
988
+ end
989
+ end
990
+
991
+ # Items: Map elsewhere
992
+ #
993
+ s_msg[/[12][36]00/].each do |s|
994
+ map_16_to_38x_item( s.descendants_and_self, item_counter,
995
+ master_data, vz )
996
+ item_counter += 2
997
+ end
998
+
999
+ # Trailer
1000
+ #
1001
+ uns = @msg.new_segment("UNS")
1002
+ uns.d0081 = 'S'
1003
+ @msg.add(uns)
1004
+
1005
+ # MOA SG50
1006
+ #
1007
+ summe_402, summe_124, summe_79 = 0, 0, 0
1008
+ # summe_77, summe_131: siehe oben
1009
+ all_str.each do |s|
1010
+ if s.b_vereinbarung_kz_1 == 2 # Nr. 27
1011
+ summe_402 += s.b_vereinbarung_1
1012
+ end
1013
+ # FIXME: Really try to add both occurrences?
1014
+ if s.b_vereinbarung_kz_2 == 2 # Nr. 27
1015
+ summe_402 += s.b_vereinbarung_2
1016
+ end
1017
+ summe_124 += s.umsatzsteuer
1018
+ summe_131 += s.rechnungsrabatt_gesamt
1019
+ summe_79 += s.warenbetrag
1020
+ # summe_77 += s.endbetrag # See beginning
1021
+ end
1022
+ @msg.add_moa( :d5025=> 402, :d5004=> vz*summe_402/100.0 ) if summe_402 != 0
1023
+ @msg.add_moa( :d5025=> 131, :d5004=> vz*summe_131/100.0 ) if summe_131 != 0
1024
+ @msg.add_moa( :d5025=> 125, :d5004=> vz*(summe_77 - summe_124)/100.0 )
1025
+ @msg.add_moa( :d5025=> 124, :d5004=> vz*summe_124/100.0 ) if summe_124 != 0
1026
+ @msg.add_moa( :d5025=> 79, :d5004=> vz*summe_79 /100.0 ) if summe_79 != 0
1027
+ @msg.add_moa( :d5025=> 77, :d5004=> vz*summe_77 /100.0 ) if summe_77 != 0
1028
+
1029
+ # TAX-MOA SG52, only if more than 1 trailer record found
1030
+ #
1031
+ all_str.each do |s|
1032
+ if s.ust_kz != 0 # Nr. 9
1033
+ @msg.add_tax( :ust_kz=> s.ust_kz, :pos_nr=> s.positions_nr )
1034
+ @msg.add_moa( :d5025=> 79, :d5004=> vz*s.warenbetrag/100.0 )
1035
+ @msg.add_moa( :d5025=> 124, :d5004=> vz*s.umsatzsteuer/100.0 )
1036
+ betrag = s.rechnungsrabatt_gesamt + s.skonto
1037
+ @msg.add_moa( :d5025=> 131, :d5004=> vz*betrag/100.0 ) if betrag != 0
1038
+ end
1039
+ end if all_str.size > 1
1040
+
1041
+ @p.add @msg, false
1042
+ end
1043
+
1044
+
1045
+ def map_16_to_38x_item( segments, item_counter, master_data, vz )
1046
+ # Lookahead / out-of-sequence segments first:
1047
+ ftx_in_item = []
1048
+ segments.find_all{|seg| seg.name=~/[12][36]08/}.each do |seg|
1049
+ ftx = @msg.new_segment("FTX")
1050
+ ftx.d4451 = 'ZZZ'
1051
+ ftx.cC108.a4440[0].value = seg.freitext.strip
1052
+ ftx.d3453 = 'DE'
1053
+ ftx_in_item << ftx
1054
+ end
1055
+
1056
+ s02 = Verkettung02_Felder.new
1057
+ if seg=segments.find{|s| s.name=~/[12][36]02/} # just 0..1 expected
1058
+ s02.ust_prozentsatz = seg.ust_prozentsatz
1059
+ s02.preisschluessel = seg.preisschluessel
1060
+ s02.komma_kz_p = seg.komma_kz_p
1061
+ s02.grundpreis = seg.grundpreis
1062
+ s02.mengenschluessel = seg.mengenschluessel
1063
+ s02.komma_kz_m = seg.komma_kz_m
1064
+ s02.menge = seg.menge
1065
+ s02.empty = false
1066
+ else
1067
+ s02.empty = true
1068
+ end
1069
+
1070
+ # Now do the serial mapping
1071
+ segments.each do |seg|
1072
+
1073
+ seg_id = seg.name
1074
+ seg_id += ' ' + seg.sg_name if seg.sg_name
1075
+ case seg_id
1076
+ when /[12][36]00 SG1/
1077
+ # FIXME: Linear search - consider a hash for fast lookup via ean.
1078
+ s5000 = master_data.find{|s| s.name=='5000' && s.ean==seg.ean}
1079
+ raise "SA 50 fehlt fuer EAN #{seg.ean}, pos_nr=#{seg.positions_nr}" unless s5000
1080
+
1081
+ @msg.add_lin( :d1082=> item_counter, :d7140=> seg.ean)
1082
+
1083
+ # PIA
1084
+ #
1085
+ @msg.add_pia( :d4347=> 1, :d7140=> s5000.artikel_nr, :d7143=> 'SA')
1086
+
1087
+ # IMD
1088
+ #
1089
+ @msg.add_imd( :d7077=> 'C', :d7009=> 'IN', :d7009_3055=> 9)
1090
+ @msg.add_imd( :d7077=> 'A', :d7008_1=> s5000.langtext)
1091
+
1092
+ # QTY
1093
+ #
1094
+ if s02.empty || s02.mengenschluessel == 0 || s02.komma_kz_m == 0
1095
+ mengenschluessel, komma_kz_m, menge =
1096
+ seg.mengenschluessel, seg.komma_kz_m, seg.menge
1097
+ else
1098
+ mengenschluessel, komma_kz_m, menge =
1099
+ s02.mengenschluessel, s02.komma_kz_m, s02.menge
1100
+ end
1101
+ if s02.empty || s02.preisschluessel == 0 || s02.komma_kz_p == 0
1102
+ preisschluessel, komma_kz_p, grundpreis =
1103
+ seg.preisschluessel, seg.komma_kz_p, seg.grundpreis
1104
+ else
1105
+ preisschluessel, komma_kz_p, grundpreis =
1106
+ s02.preisschluessel, s02.komma_kz_p, s02.grundpreis
1107
+ end
1108
+ @msg.add_qty( :d6063=> preisschluessel == 9 ? 192 : 47, # w/o charge?
1109
+ :d6060=> to_value( komma_kz_m, vz*menge ),
1110
+ :qkey=> mengenschluessel )
1111
+
1112
+ # All FTX
1113
+ #
1114
+ ftx_in_item.each {|obj| @msg.add(obj)}
1115
+
1116
+ # MOA
1117
+ #
1118
+ betrag = seg.artikelrabatt_gesamt
1119
+ @msg.add_moa( :d5025=> 131, :d5004=> vz*betrag/100.0 ) if betrag != 0
1120
+ betrag = seg.warenwert
1121
+ @msg.add_moa( :d5025=> 203, :d5004=> vz*betrag/100.0 ) if betrag != 0
1122
+ if seg.b_vereinbarung_kz_2 == 3
1123
+ @msg.add_moa( :d5025=> 204, :d5004=> vz*seg.b_vereinbarung_2/10.0 )
1124
+ end
1125
+
1126
+ # PRI
1127
+ #
1128
+ pri_aab =
1129
+ @msg.add_pri(:d5125=>'AAB', :d5118=>to_value( komma_kz_p,grundpreis),
1130
+ :pkey=>preisschluessel, :qkey=>mengenschluessel )
1131
+ if seg.schluessel_kz == 1
1132
+ @msg.add_pri( :d5125=>'AAE',
1133
+ :d5118=>to_value( seg.komma_kz, seg.sondereintrag ) )
1134
+ # :pkey=>preisschluessel, :qkey=>mengenschluessel)
1135
+ elsif seg.schluessel_kz == 3
1136
+ @msg.add_pri( :d5125=>'AAA',
1137
+ :d5118=>to_value( seg.komma_kz, seg.sondereintrag ) )
1138
+ end
1139
+
1140
+ # RFF-DTM SG30
1141
+ #
1142
+ if seg.b_vereinbarung_kz_2 == 5
1143
+ @msg.add_rff( :d1153=>'AAK', :d1154=> seg.b_vereinbarung_2 )
1144
+ end
1145
+
1146
+ # TAX-MOA
1147
+ #
1148
+ @msg.add_tax( :ust_kz=> seg.umsatzsteuer_kz, :pos_nr=> seg.positions_nr )
1149
+
1150
+
1151
+ # ALC SG39
1152
+ #
1153
+ [[seg.rabatt_kz_1, seg.artikelrabatt_1, 1],
1154
+ [seg.rabatt_kz_2, seg.artikelrabatt_2, 2],
1155
+ [seg.rabatt_kz_3, seg.artikelrabatt_3, 3],
1156
+ [seg.rabatt_kz_4, seg.artikelrabatt_4, 4]].each do |a|
1157
+ kz, betrag, no = a
1158
+ next if kz == 0
1159
+
1160
+ params = {
1161
+ :d5463=> betrag < 0 ? 'A' : 'C',
1162
+ :d7161=> 'DI', :d1227=> kz%10 - 1
1163
+ }
1164
+ params[:d1227] = nil unless params[:d1227].between?(1,4)
1165
+ @msg.add_alc( params )
1166
+
1167
+ case (kz - kz%10)/10
1168
+ when 1..4, 6
1169
+ @msg.add_moa( :d5025=> 8, :d5004=> betrag.abs/100.0 )
1170
+ when 5
1171
+ @msg.add_moa( :d5025=> 8, :d5004=> betrag.abs/100.0 )
1172
+ pri_aab.d5125 = 'AAA' if pri_aab # Why?
1173
+ when 7
1174
+ @msg.add_pcd( :d5245=> 3, :d5482=> betrag.abs/10000.0 )
1175
+ when 8
1176
+ @msg.add_rte( :d5419=> 1, :d5420=> betrag.abs/10000.0 )
1177
+ else
1178
+ raise "ALC: Unbekanntes 'Rabatt-Kennzeichen: '#{kz}'"
1179
+ end
1180
+ end
1181
+
1182
+ # Sub-line info
1183
+ #
1184
+ @msg.add_lin( :d1082=> item_counter+1,
1185
+ :d7140=> s5000.kleinste_ean,
1186
+ :c829_d1082 => item_counter )
1187
+
1188
+ @msg.add_pia( :d4347=> 1, :d7140=> "%04d" % s5000.klassifikation,
1189
+ :d7143=> 'GN')
1190
+ @msg.add_imd( :d7077=> 'C', :d7009=> 'CU', :d7009_3055=> 9)
1191
+ @msg.add_qty( :d6063=> 59, :d6060=> s5000.menge_ean )
1192
+
1193
+
1194
+ when /[12][36]0[2468] SG1/
1195
+ # ignored here - handled by lookahead section
1196
+
1197
+ else
1198
+ raise "Segment-Id #{seg_id} hier nicht erwartet!"
1199
+ end
1200
+ end
1201
+ end
1202
+
1203
+
1204
+ def map_29_to_393( s_msg )
1205
+ @msg = @p.new_message( @msg_params )
1206
+
1207
+ raise "Verkettung 01 zu SA29 nicht unterstuetzt - sorry" unless s_msg['2901'].empty?
1208
+ raise "Verkettung 02 zu SA29 nicht unterstuetzt - sorry" unless s_msg['2902'].empty?
1209
+
1210
+ all_s29 = s_msg['2900']
1211
+ s29 = all_s29.first # Must exist due to SEDAS msg definition!
1212
+
1213
+ bgm = @msg.new_segment("BGM")
1214
+ bgm.cC002.d1001 = 393
1215
+ raise "Erste SA29 muss eine 'Rechnungslistennummer' enthalten!" unless s29.reli_kz == 2
1216
+ bgm.cC106.d1004 = s29.nummer
1217
+ bgm.d1225 = 9
1218
+ @msg.add(bgm)
1219
+
1220
+ @msg.add_dtm(:d2005=> 137, :digits=> 5, :d2380=>s29.datum_reli)
1221
+
1222
+ # SG1: RFF-DTM
1223
+ #
1224
+ if (nr=s29.abkommen_nr) != 0
1225
+ @msg.add_rff( :d1153=>'CT', :d1154=> nr )
1226
+ end
1227
+
1228
+ # SG2: NAD group
1229
+ #
1230
+ @msg.add_nad( :d3035=>'SU', :d3039=> s29.bbn_lieferant )
1231
+ @msg.add_nad( :d3035=>'BY', :d3039=> s29.bbn_rechnungslistenempfaenger )
1232
+ @msg.add_nad( :d3035=>'PE', :d3039=> s29.bbn_zahlungsempfaenger )
1233
+ @msg.add_nad( :d3035=>'PR', :d3039=> s29.bbn_zahlungsleistender )
1234
+
1235
+
1236
+ # TAX-MOA SG6
1237
+ #
1238
+ @msg.add_tax( :ust_kz=> s29.ust_kz, :pos_nr=> s29.positions_nr )
1239
+ # Just first occurrence of SA29 ok?
1240
+
1241
+ # PAT-DTM SG8
1242
+ #
1243
+ if (vdat=s29.valutadatum) != 0
1244
+ @msg.add_pat( :d4279=> 3 )
1245
+ @msg.add_dtm( :d2005=> 209, :d2380=> vdat )
1246
+ end
1247
+
1248
+
1249
+ # Trailer
1250
+ #
1251
+ uns = @msg.new_segment("UNS")
1252
+ uns.d0081 = 'S'
1253
+ @msg.add(uns)
1254
+
1255
+ # MOA SG50
1256
+ #
1257
+
1258
+ summe_124, summe_125, summe_86 = summe_9 = 0, 0, 0, 0
1259
+ all_s29.each do |s|
1260
+ summe_124 += s.umsatzsteuer
1261
+ summe_86 += s.endbetrag
1262
+ summe_9 += s.zahlbetrag
1263
+ end
1264
+ @msg.add_moa( :d5025=> 86, :d5004=> summe_86 /100.0 )
1265
+ @msg.add_moa( :d5025=> 9, :d5004=> summe_9 /100.0 )
1266
+ @msg.add_moa( :d5025=> 124, :d5004=> summe_124/100.0 )
1267
+ @msg.add_moa( :d5025=> 125, :d5004=> (summe_86 - summe_124)/100.0 )
1268
+
1269
+
1270
+ # TAX-MOA SG52, only if more than 1 S29 record found
1271
+ #
1272
+ all_s29.each do |s|
1273
+ @msg.add_tax( :ust_kz=> s.ust_kz, :pos_nr=> s.positions_nr )
1274
+ @msg.add_moa( :d5025=> 124, :d5004=> s.umsatzsteuer/100.0 )
1275
+ @msg.add_moa( :d5025=> 125, :d5004=> (s.zahlbetrag - s.umsatzsteuer)/100.0 )
1276
+ @msg.add_moa( :d5025=> 86, :d5004=> s.endbetrag/100.0 )
1277
+ end if all_s29.size > 1
1278
+
1279
+ @p.add @msg
1280
+ end
1281
+
1282
+
1283
+ def initialize(src, dest)
1284
+ @msg = nil
1285
+ @with_ung = false
1286
+ @s = src
1287
+ @ic = dest
1288
+ @p = @ic # Current parent of a message - either the interchange or a group
1289
+ @msg_params = {
1290
+ :msg_type => 'INVOIC',
1291
+ :version => 'D',
1292
+ :release => '01B',
1293
+ :resp_agency => 'UN',
1294
+ :assigned_code => 'EAN010'
1295
+ }
1296
+ @curr_year = Time.now.year.to_s
1297
+ end
1298
+
1299
+
1300
+ def go
1301
+ map_00_to_unb(@s.header)
1302
+ @s.each do |grp|
1303
+ map_01_to_ung(grp.header) if self.with_ung
1304
+ master_data = grp.find {|msg| msg.name=='50'}
1305
+ grp.each do |msg|
1306
+ case msg.name
1307
+ when '12', '22'
1308
+ map_12_to_325( msg, master_data )
1309
+ when '15', '25'
1310
+ map_15_to_38x( msg, master_data )
1311
+ when '29'
1312
+ map_29_to_393( msg )
1313
+ when '50'
1314
+ # Skip master data
1315
+ else
1316
+ raise "SA#{msg.name} nicht unterstuetzt - sorry"
1317
+ end
1318
+ end
1319
+ end
1320
+ end
1321
+
1322
+
1323
+ def validate
1324
+ @ic.validate
1325
+ end
1326
+
1327
+ def write(hnd)
1328
+ @ic.write(hnd)
1329
+ end
1330
+ end
1331
+
1332
+
1333
+ #
1334
+ # MAIN
1335
+ #
1336
+
1337
+ $log = Logger.new(STDERR)
1338
+ $log.level = Logger::INFO
1339
+ $log.datetime_format = "%H:%M:%S"
1340
+
1341
+ params = {
1342
+ :show_una => true,
1343
+ :charset => 'UNOC',
1344
+ :version => 3,
1345
+ :interchange_control_reference => Time.now.to_f.to_s[0...14] ,
1346
+ # :application_reference => 'EANCOM' ,
1347
+ # :output_mode => :verbatim,
1348
+ # :acknowledgment_request => true,
1349
+ :interchange_agreement_id => 'EANCOM'+'' , # your ref here!
1350
+ :test_indicator => 1,
1351
+ }
1352
+ ic = EDI::E::Interchange.new( params )
1353
+
1354
+
1355
+ with_ung = false
1356
+
1357
+ while ARGV[0] =~ /^-(\w)/
1358
+ opt = ARGV.shift
1359
+ case $1
1360
+ when 'v' # verbose mode - here: use formatted output
1361
+ ic.output_mode = :indented
1362
+ when 'g'
1363
+ with_ung = true
1364
+ else
1365
+ raise "Option nicht zulaessig: #{opt}"
1366
+ end
1367
+ end
1368
+
1369
+ $log.info "Input einlesen..."
1370
+ sedas_ic = EDI::S::Interchange.parse(File.open(ARGV[0], 'r'), false)
1371
+ $log.info "Input validieren..."
1372
+ sedas_ic.validate
1373
+ $log.info "Zuordnen..."
1374
+ map = SEDAS_to_EANCOM02_Map_INVOIC.new( sedas_ic, ic )
1375
+ #
1376
+ # Add UNG/UNE if there is more than one group per file
1377
+ #
1378
+ map.with_ung = (sedas_ic.size > 1) # with_ung
1379
+ map.go
1380
+ $log.info "Output validieren..."
1381
+ ic.validate
1382
+ $log.info "Ergebnis schreiben..."
1383
+ $stdout.write ic
1384
+ $log.info "Fertig."
1385
+ # ic.inspect