efo_nelfo 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b1766eab8e78076ddd1e00b0bd831975ab51cf86
4
+ data.tar.gz: d0188d51f9e52fd7bd133717261540334d2abf3f
5
+ SHA512:
6
+ metadata.gz: 2f4522a7ac95b3655348404e92b8dab3bd79d8787b9c4128033e7e2f5d8c8b1659730144c8a136bb17f0b33438e9e1af2c9107c26ed8d1a65a17f1b0b594afcb
7
+ data.tar.gz: 6708ebf094bb145c2a30f7a4e7c4d81887fdcdd5d1603b1c470898ba76a466eb86fb4b86f81edb05f6400128a9c1fcf49d55e64d1d33eb739a88e4f4ab7f08a0
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in efo_nelfo.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'minitest' do
5
+ watch(%r|^spec/(.*)_spec\.rb|)
6
+ watch(%r|^lib/(.*)([^/]+)\.rb|) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
7
+ watch(%r|^spec/spec_helper\.rb|) { "spec" }
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Gudleik Rasch
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # EfoNelfo
2
+
3
+ Gem for parsing and writing EfoNelfo documents.
4
+
5
+ Supported versions:
6
+
7
+ * 4.0
8
+
9
+ Supported formats:
10
+
11
+ * Bestilling
12
+
13
+
14
+ ## Usage
15
+
16
+ Importing a CSV file:
17
+
18
+ # EfoNelfo.parse_csv "B12345678.332.csv" # => EfoNelfo::V40::Order
19
+
20
+ Exporting CSV:
21
+
22
+ # order = EfoNelfo::V40::Order.new
23
+ # order.add EfoNelfo::V40::Order::Line.new item_number: '442', order_number: 'abc'
24
+ # order.to_csv
25
+
26
+
27
+ ## TODO
28
+
29
+ * Export to json
30
+ * Support more filetypes
31
+ * Support more versions
32
+ * Support XML
33
+
34
+
35
+ ## Resources
36
+
37
+ * http://www.efo.no/Portals/5/docs/ImplementasjonsGuide%20EFONELFO%204.0.pdf
38
+
39
+
40
+ ## Contributing
41
+
42
+ 1. Fork it
43
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
44
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
45
+ 4. Push to the branch (`git push origin my-new-feature`)
46
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'spec'
6
+ t.test_files = FileList['spec/*_spec.rb']
7
+ t.verbose = false
8
+ end
data/efo_nelfo.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'efo_nelfo/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "efo_nelfo"
8
+ spec.version = EfoNelfo::VERSION
9
+ spec.authors = ["Gudleik Rasch"]
10
+ spec.email = ["gr@skalar.no"]
11
+ spec.description = %q{Parser for EFONELFO format}
12
+ spec.summary = %q{Parser for EFONELFO format}
13
+ spec.homepage = "http://efo.no"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler" #, "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "awesome_print"
24
+ spec.add_development_dependency "guard-minitest"
25
+ spec.add_development_dependency "rb-fsevent"
26
+ spec.add_development_dependency "terminal-notifier-guard"
27
+ end
@@ -0,0 +1,10 @@
1
+ module EfoNelfo
2
+
3
+ class Array < ::Array
4
+ def to_a
5
+ map(&:to_a).flatten(1)
6
+ end
7
+ end
8
+
9
+ end
10
+
@@ -0,0 +1,13 @@
1
+ module EfoNelfo
2
+
3
+ module AttributeAssignment
4
+ def initialize_attributes(*args)
5
+ if args && args.first.is_a?(Hash)
6
+ args.first.each do |attr, value|
7
+ send "#{attr}=", value
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,4 @@
1
+ module EfoNelfo
2
+ class UnsupportedPostType < StandardError; end
3
+ class DuplicateProperty < StandardError; end
4
+ end
@@ -0,0 +1,26 @@
1
+ class Order
2
+ def initialize
3
+ @lines = [ ['c', 'd'] ]
4
+ @attrs = [ 'a', 'b' ]
5
+ end
6
+
7
+ def to_a
8
+ # [ @attrs ] << lines
9
+ arr = [ @attrs ]
10
+ lines.each do |i|
11
+ arr += i
12
+ end
13
+ arr
14
+ end
15
+
16
+ def lines
17
+ @lines
18
+ end
19
+
20
+ def to_csv
21
+ to_a
22
+ end
23
+ end
24
+
25
+
26
+ puts Order.new.to_csv.inspect
@@ -0,0 +1,51 @@
1
+ module EfoNelfo
2
+
3
+ class PostType
4
+ include EfoNelfo::Property
5
+
6
+ attr_reader :post_type
7
+
8
+ @modules = []
9
+
10
+ class << self
11
+ def inherited(klass)
12
+ @modules << klass
13
+ end
14
+
15
+ def for(type, version=nil)
16
+ @modules.select { |mod| mod.can_parse?(type, version) }.first
17
+ end
18
+
19
+ def can_parse?(post_type, check_version=nil)
20
+ if check_version
21
+ self::POST_TYPES.keys.include?(post_type) && check_version == version
22
+ else
23
+ self::POST_TYPES.keys.include?(post_type)
24
+ end
25
+ end
26
+
27
+ # Extracts version number from class namespace.
28
+ # Example: EfoNelfo::V41::Some::Class.version # => "4.1"
29
+ def version
30
+ (self.to_s.match(/::V(?<version>\d+)::/)[:version].to_f / 10).to_s
31
+ end
32
+
33
+ end
34
+
35
+ def initialize(*args)
36
+ initialize_attributes *args
37
+ @post_type = self.class::POST_TYPES.keys.first
38
+ @version = self.class.version
39
+ end
40
+
41
+ # This is for adding posttypes
42
+ def add(something)
43
+ end
44
+
45
+ def post_type_human
46
+ self.class::POST_TYPES[post_type]
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,132 @@
1
+ module EfoNelfo
2
+
3
+ module Property
4
+ include EfoNelfo::AttributeAssignment
5
+
6
+ def self.included(base)
7
+ base.send :extend, ClassMethods
8
+ end
9
+
10
+ def attributes
11
+ @attributes ||= initialize_default_attributes
12
+ end
13
+
14
+ def properties
15
+ self.class.properties
16
+ end
17
+
18
+ def to_a
19
+ properties.keys.map { |prop| formatted_for_csv(prop) }
20
+ end
21
+
22
+ private
23
+
24
+ def initialize_default_attributes
25
+ properties.inject({}) { |h,(name,options)| h[name] = options[:default]; h }
26
+ end
27
+
28
+ def format_value(value, type)
29
+ case type
30
+ when :integer
31
+ value.nil? ? nil : value.to_i
32
+ when :date
33
+ if value.nil?
34
+ nil
35
+ elsif value.kind_of? String
36
+ Date.parse value
37
+ else
38
+ value
39
+ end
40
+ when :boolean
41
+ value.nil? || value == true || value == 'J' || value == '' ? true : false
42
+ else
43
+ value
44
+ end
45
+ end
46
+
47
+ def formatted_for_csv(attr)
48
+ value = send(attr)
49
+
50
+ type = properties[attr][:type]
51
+ case type
52
+ when :date
53
+ value ? value.strftime("%Y%m%d") : nil
54
+ when :boolean
55
+ value == true ? "J" : nil
56
+ else
57
+ value
58
+ end
59
+ end
60
+
61
+ module ClassMethods
62
+ # Creates an attribute with given name.
63
+ #
64
+ # Options
65
+ # - type String, Integer etc. Default is String
66
+ # - required whether attribute is required. Default is false
67
+ # - limit Length the attribute can be. Default is nil
68
+ # - alias Norwegian alias name for the attribute
69
+ #
70
+ def property(name, options={})
71
+ options = {
72
+ type: :string,
73
+ required: false,
74
+ }.update options
75
+
76
+ name = name.to_sym
77
+
78
+ # Store property info in @properties
79
+ raise EfoNelfo::DuplicateProperty if properties.has_key?(name)
80
+ properties[name] = options
81
+
82
+ create_reader_for(name, options)
83
+ create_setter_for(name, options) unless options[:read_only]
84
+ create_question_for(name) if options[:type] == :boolean
85
+ create_alias_for(name, options) if options[:alias]
86
+ end
87
+
88
+ # Returns all properties
89
+ def properties
90
+ @_properties ||= {}
91
+ end
92
+
93
+ private
94
+
95
+ # Creates an attribute accessor for name
96
+ def create_reader_for(name, options)
97
+ define_method name do
98
+ attributes[name]
99
+ end
100
+ end
101
+
102
+ # Creates an attribute setter for name
103
+ def create_setter_for(name, options)
104
+ define_method "#{name}=" do |value|
105
+ attributes[name] = format_value(value, options[:type])
106
+ end
107
+ end
108
+
109
+ # Creates a name? accessor
110
+ def create_question_for(name)
111
+ define_method "#{name}?" do
112
+ attributes[name] == true
113
+ end
114
+ end
115
+
116
+ def create_alias_for(name, options)
117
+ define_method(options[:alias]) do
118
+ send name
119
+ end
120
+
121
+ unless options[:read_only]
122
+ define_method("#{options[:alias]}=") do |val|
123
+ send "#{name}=", val
124
+ end
125
+ end
126
+ end
127
+
128
+ end
129
+
130
+ end
131
+
132
+ end
@@ -0,0 +1,67 @@
1
+ require 'csv'
2
+
3
+ module EfoNelfo
4
+ module Reader
5
+
6
+ class CSV
7
+ CSV_OPTIONS = {
8
+ col_sep: ';',
9
+ headers: false,
10
+ row_sep: "\r\n",
11
+ encoding: "iso-8859-1",
12
+ quote_char: "\x00",
13
+ force_quotes: false,
14
+ skip_blanks: true
15
+ }
16
+
17
+ attr_reader :csv, :data
18
+
19
+ def initialize(options)
20
+ if options[:filename]
21
+ @data = File.read(options[:filename], encoding: CSV_OPTIONS[:encoding])
22
+ else
23
+ @data = options[:data]
24
+ end
25
+
26
+ @csv = ::CSV.new @data, CSV_OPTIONS
27
+ end
28
+
29
+ def parse
30
+ # Create the head object based on the first row
31
+ head = parse_head csv.first
32
+ head.source = @data
33
+
34
+ # Read rest of the file and add them to the head
35
+ csv.each do |row|
36
+ # Find the correct posttype module for given posttype and version
37
+ klass = EfoNelfo::PostType.for row[0]
38
+ next if klass.nil?
39
+
40
+ line = initialize_object_with_properties klass, row
41
+ head.add line
42
+ end
43
+
44
+ head
45
+ end
46
+
47
+ private
48
+
49
+ def parse_head(row)
50
+ klass = EfoNelfo::PostType.for row[0], row[2]
51
+ raise EfoNelfo::UnsupportedPostType.new("Don't know how to handle v#{row[2]} of #{row[0]}") if klass.nil?
52
+
53
+ initialize_object_with_properties klass, row
54
+ end
55
+
56
+ def initialize_object_with_properties(klass, columns)
57
+ object = klass.new
58
+ object.class.properties.each_with_index do |property, i|
59
+ object.send "#{property.first}=", columns[i]
60
+ end
61
+ object
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+ module EfoNelfo
3
+ module V40
4
+ class Order
5
+ class Line < EfoNelfo::PostType
6
+ POST_TYPES = {
7
+ 'BL' => 'Bestilling vareLinjepost',
8
+ 'IL' => 'Forespørsel vareLinjepost'
9
+ }
10
+
11
+ # It's important to list the property in the same order as specified in the specs
12
+ property :post_type, alias: :PostType, limit: 2, default: 'BL'
13
+ property :index, alias: :LinjeNr, limit: 4, type: :integer
14
+ property :order_number, alias: :BestNr, limit: 10, required: true
15
+ property :item_type, alias: :VareMrk, limit: 1, required: true, type: :integer
16
+ property :item_number, alias: :VareNr, limit: 14, required: true
17
+ property :item_name, alias: :VaBetg, limit: 30, required: true
18
+ property :item_description, alias: :VaBetg2, limit: 30
19
+ property :item_count, alias: :Ant, limit: 9, required: true, type: :integer
20
+ property :price_unit, alias: :PrisEnhet, limit: 3, required: true
21
+ property :buyer_item_number, alias: :KVareNr, limit: 25
22
+ property :delivery_date, alias: :LevDato, type: :date
23
+ property :buyer_ref, alias: :KjøpersRef, limit: 25
24
+ property :splitable, alias: :DelLev, type: :boolean, default: true
25
+ property :replacable, alias: :AltKode, type: :boolean, default: true
26
+
27
+ attr_accessor :text
28
+
29
+ # Returns an array with one or more elements
30
+ def to_a
31
+ [ super, text.to_a ].reject(&:empty?)
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,102 @@
1
+ # encoding: utf-8
2
+ module EfoNelfo
3
+ module V40
4
+ class Order < EfoNelfo::PostType
5
+ POST_TYPES = {
6
+ 'BH' => 'Bestilling Hodepost',
7
+ 'IH' => 'Forespørsel Hodepost'
8
+ }
9
+
10
+ attr_reader :lines
11
+ attr_accessor :source
12
+
13
+ # It's important to list the property in the same order as specified in the specs
14
+ property :post_type, alias: :PostType, limit: 2, default: 'BH'
15
+ property :format, alias: :Format, limit: 8, default: 'EFONELFO'
16
+ property :version, alias: :Versjon, limit: 3, default: version
17
+ property :seller_id, alias: :SelgersId, limit: 14
18
+ property :buyer_id, alias: :KjøpersId, limit: 14, required: true
19
+ property :order_number, alias: :BestNr, limit: 10, required: true
20
+ property :customer_id, alias: :KundeNr, limit: 10, required: true
21
+ property :contract_type, alias: :AvtaleIdMrk, limit: 1, type: :integer
22
+ property :contract_id, alias: :AvtaleId, limit: 10
23
+ property :buyer_order_number, alias: :KOrdNr, limit: 10
24
+ property :buyer_customer_id, alias: :KundAvd, limit: 10
25
+ property :project_id, alias: :ProsjektNr, limit: 10
26
+ property :buyer_warehouse_location, alias: :KLagerMrk, limit: 1
27
+ property :buyer_warehouse, alias: :KLager, limit: 14
28
+ property :seller_warehoure_location, alias: :SLagerMrk, limit: 1
29
+ property :seller_warehouse, alias: :SLager, limit: 14
30
+ property :external_ref, alias: :EksternRef, limit: 36
31
+ property :buyer_ref, alias: :KjøpersRef, limit: 25
32
+ property :label, alias: :Merket, limit: 25
33
+ property :confirmation_type, alias: :ObkrType, limit: 2
34
+ property :transport_type, alias: :TransportMåte, limit: 25
35
+ property :transport_msg, alias: :Melding, limit: 25
36
+ property :delivery_date, alias: :LevDato, type: :date
37
+ property :origin, alias: :BestOpp, limit: 2
38
+
39
+ property :receiver_delivery_location, alias: :LAdrLok, limit: 14
40
+ property :receiver_name, alias: :LFirmaNavn, limit: 35
41
+ property :receiver_address1, alias: :LAdr1, limit: 35
42
+ property :receiver_address2, alias: :LAdr2, limit: 35
43
+ property :receiver_zip, alias: :LPostNr, limit: 9
44
+ property :receiver_office, alias: :LPostSted, limit: 35
45
+ property :receiver_country, alias: :LLandK, limit: 2
46
+
47
+ property :buyer_name, alias: :KFirmaNavn, limit: 35
48
+ property :buyer_address1, alias: :KAdr1, limit: 35
49
+ property :buyer_address2, alias: :KAdr2, limit: 35
50
+ property :buyer_zip, alias: :KPostNr, limit: 9
51
+ property :buyer_office, alias: :KPostSted, limit: 35
52
+ property :buyer_country, alias: :KLandK, limit: 2
53
+ property :buyer_email, alias: :KEPost, limit: 60
54
+ property :buyer_web, alias: :KWebAdr, limit: 40
55
+
56
+ property :seller_name, alias: :SFirmaNavn, limit: 35
57
+ property :seller_address1, alias: :SAdr1, limit: 35
58
+ property :seller_address2, alias: :SAdr2, limit: 35
59
+ property :seller_zip, alias: :SPostNr, limit: 9
60
+ property :seller_office, alias: :SPostSted, limit: 35
61
+ property :seller_country, alias: :SLandK, limit: 2
62
+
63
+ def initialize(*args)
64
+ super
65
+ @lines = EfoNelfo::Array.new
66
+ end
67
+
68
+ def add(post_type)
69
+ case
70
+ when post_type.is_a?(Order::Line) then add_order_line(post_type)
71
+ when post_type.is_a?(Order::Text) then add_text_to_order_line(post_type)
72
+ end
73
+ end
74
+
75
+ def to_a
76
+ [ super ] + lines.to_a
77
+ end
78
+
79
+ def to_csv
80
+ CSV.generate EfoNelfo::Reader::CSV::CSV_OPTIONS do |csv|
81
+ to_a.each do |row|
82
+ csv << row unless row.empty?
83
+ end
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ # Appends a order line to the order
90
+ def add_order_line(line)
91
+ line.index = lines.length + 1
92
+ lines << line
93
+ end
94
+
95
+ # Add text to the last added orderline
96
+ def add_text_to_order_line(text)
97
+ lines.last.text = text
98
+ end
99
+
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ module EfoNelfo
3
+ module V40
4
+ class Order
5
+ class Text < EfoNelfo::PostType
6
+ POST_TYPES = {
7
+ 'BT' => 'Bestilling Fritekstlinje',
8
+ 'IT' => 'Forespørsel Fritekstlinje'
9
+ }
10
+
11
+ # It's important to list the property in the same order as specified in the specs
12
+ property :post_type, alias: :PostType, limit: 2, default: 'BT'
13
+ property :text, alias: :FriTekst, limit: 30
14
+
15
+ def to_s
16
+ text
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module EfoNelfo
2
+ VERSION = "0.0.1"
3
+ end
data/lib/efo_nelfo.rb ADDED
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ # Common stuff
4
+ require 'efo_nelfo/version'
5
+ require 'efo_nelfo/errors'
6
+ require 'efo_nelfo/array'
7
+ require 'efo_nelfo/attribute_assignment'
8
+ require 'efo_nelfo/property'
9
+ require 'efo_nelfo/post_type'
10
+
11
+ # EfoNelfo v4.0 modules
12
+ require 'efo_nelfo/v40/order/order'
13
+ require 'efo_nelfo/v40/order/line'
14
+ require 'efo_nelfo/v40/order/text'
15
+
16
+ # Reader modules (import)
17
+ require 'efo_nelfo/reader/csv'
18
+
19
+ module EfoNelfo
20
+
21
+ class << self
22
+
23
+ def load(filename)
24
+ Reader::CSV.new(filename: filename).parse
25
+ end
26
+
27
+ def parse(data)
28
+ Reader::CSV.new(data: data).parse
29
+ end
30
+
31
+ end
32
+
33
+
34
+ end
35
+