sie 2.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OTRlMWFiNTRkMmRjZDllMmY2MWYzYjFiMWYxZWI1MTNjNDNhODZkYw==
4
+ YTQzMmU0NGU3NmVhMDY1OWM0OWJmZWMxY2I3M2IyZDJjZTVmMDRiYQ==
5
5
  data.tar.gz: !binary |-
6
- MzliZThiOGNhNjZmODMyNzVkNTJhZWY0NzYzOTRjMGIyMjFkYTM1Zg==
6
+ MWNlN2E5OWE3ZGRmZmZkNmRiZmI0MGVkNGZmMThlMDI3M2EwZjE3Yw==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ODJiZjc3ZDI5YWFiN2YxZTg0NTEwMTNjYjVjZDI4NjhlYWJhOGM4YzUyZjNi
10
- Y2QxMDA2YmI4MjFiMDU3OWFhMjE5NmNjZTM2ZTRjYTdlMzFiOTA5OTcyNmQ4
11
- ODdmNDAwYjZmNDRmZmM2NjY4ZDFhYzI0NTEwZDcyYTFkYmVkNzM=
9
+ NGY1ZTViZWQyNGUwMzVkNjhlMDI3ZTk4NDYyODg2MjZjYTY2M2Y5YzI0NzI1
10
+ NTNhODYzZmEwMWQ1ZjExYmU3YTVlMzNmMzAxNDNkNTkzOWQzMThjNGY1OTE2
11
+ YzQxM2Y1ZGJhZWQ5ZDhiMzljZDk4OWQ0MmVjYjliM2RjYWE0ZGQ=
12
12
  data.tar.gz: !binary |-
13
- MzBlMzIzZTAyMGVkYTE1YWYyM2E4MDQ1NjM0MjMyMmU1Yjg3NGQ5MzBiMjFj
14
- ZGJlMDY1MjhhZGJiZTQ4NTRmNzg0OTFkMGNmMjA5MTlmMjdmOGU0N2M1NzMz
15
- NjI5ZjFjYzBhMTg5YzFlMTVmMjg1MGM0ZWY5YjQzZDIxODNkN2Q=
13
+ MTUyMjk5NmMwYzZkZTEyZmU5YTM3MGMwYWQ5YzQ5YzQ1OGFmODU5MmY2ODI5
14
+ NmVkNTE4ZjcwNDU4YjRjZmVkNTk3MDA3ZWYyOTY0NmEyMmE0OWI1NWQxMTMz
15
+ NWU3YjhlNWEwMDY5OTNlNzQzYWU3ODYzZjdlN2NhNzY1YzVhOGQ=
data/.travis.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.2.1
3
4
  - 2.0.0
4
- - 1.9.3
data/README.md CHANGED
@@ -64,6 +64,18 @@ class YourDataSource
64
64
  [ 3100 ]
65
65
  end
66
66
 
67
+ def dimensions
68
+ [
69
+ {
70
+ number: 6,
71
+ description: "Projekt",
72
+ objects: [
73
+ { number: 1, description: "Education" }
74
+ ]
75
+ }
76
+ ]
77
+ end
78
+
67
79
  # Used to calculate balance before (and on) the given date for an account.
68
80
  def balance_before(account_number, date)
69
81
  # ActiveRecord example:
@@ -90,7 +102,8 @@ class YourDataSource
90
102
  },
91
103
  {
92
104
  account_number: 3100, amount: -512.0,
93
- booked_on: Date.today, description: "Item 1"
105
+ booked_on: Date.today, description: "Item 1",
106
+ dimensions: { 6 => 1 }
94
107
  },
95
108
  ]
96
109
  }
data/lib/sie/document.rb CHANGED
@@ -14,6 +14,7 @@ module Sie
14
14
  add_header
15
15
  add_financial_years
16
16
  add_accounts
17
+ add_dimensions
17
18
  add_balances
18
19
  add_vouchers
19
20
 
@@ -24,7 +25,7 @@ module Sie
24
25
 
25
26
  delegate :program, :program_version, :generated_on, :company_name,
26
27
  :accounts, :balance_account_numbers, :closing_account_numbers,
27
- :balance_before, :each_voucher,
28
+ :balance_before, :each_voucher, :dimensions,
28
29
  to: :data_source
29
30
 
30
31
  def add_header
@@ -66,6 +67,20 @@ module Sie
66
67
  end
67
68
  end
68
69
 
70
+ def add_dimensions
71
+ dimensions.each do |dimension|
72
+ dimension_number = dimension.fetch(:number)
73
+ dimension_description = dimension.fetch(:description)
74
+ add_line("DIM", dimension_number, dimension_description)
75
+
76
+ dimension.fetch(:objects).each do |object|
77
+ object_number = object.fetch(:number)
78
+ object_description = object.fetch(:description)
79
+ add_line("OBJEKT", dimension_number, object_number, object_description)
80
+ end
81
+ end
82
+ end
83
+
69
84
  def add_vouchers
70
85
  each_voucher do |voucher|
71
86
  add_voucher(voucher)
@@ -90,17 +105,18 @@ module Sie
90
105
  account_number = line.fetch(:account_number)
91
106
  amount = line.fetch(:amount)
92
107
  booked_on = line.fetch(:booked_on)
108
+ dimensions = line.fetch(:dimensions, {}).flatten
93
109
  # Some SIE-importers (fortnox) cannot handle descriptions longer than 30 characters,
94
110
  # but the specification has no limit.
95
111
  description = line.fetch(:description).slice(0, DESCRIPTION_LENGTH_MAX)
96
112
 
97
- add_line("TRANS", account_number, Renderer::EMPTY_ARRAY, amount, booked_on, description)
113
+ add_line("TRANS", account_number, dimensions, amount, booked_on, description)
98
114
 
99
115
  # Some consumers of SIE cannot handle single voucher lines (fortnox), so add another empty one to make
100
116
  # it balance. The spec just requires the sum of lines to be 0, so single lines with zero amount would conform,
101
117
  # but break for these implementations.
102
118
  if voucher_lines.size < 2 && amount.zero?
103
- add_line("TRANS", account_number, Renderer::EMPTY_ARRAY, amount, booked_on, description)
119
+ add_line("TRANS", account_number, dimensions, amount, booked_on, description)
104
120
  end
105
121
  end
106
122
  end
@@ -1,7 +1,6 @@
1
1
  require "stringio"
2
2
 
3
3
  class Sie::Document::Renderer
4
- EMPTY_ARRAY = :empty_array
5
4
  ENCODING = Encoding::CP437
6
5
 
7
6
  def initialize
@@ -44,8 +43,9 @@ class Sie::Document::Renderer
44
43
  case value
45
44
  when Date
46
45
  value.strftime("%Y%m%d")
47
- when EMPTY_ARRAY
48
- "{}"
46
+ when Array
47
+ subvalues = value.map { |subvalue| format_value(subvalue.to_s) }
48
+ "{#{subvalues.join(' ')}}"
49
49
  when Numeric
50
50
  value.to_s
51
51
  else
@@ -19,38 +19,58 @@ module Sie
19
19
 
20
20
  def build_complete_entry
21
21
  entry = build_empty_entry
22
- entry_type = first_token.entry_type
23
22
 
24
- entry_type.each_with_index do |entry_type, i|
25
- break if i >= tokens.size
23
+ attributes_with_tokens.each do |attr, *attr_tokens|
24
+ label = attr.is_a?(Hash) ? attr.fetch(:name) : attr
26
25
 
27
- if entry_type.is_a?(Hash)
28
- skip_array(tokens, i)
29
- next
26
+ if attr_tokens.size == 1
27
+ entry.attributes[label] = attr_tokens.first
30
28
  else
31
- label = entry_type
32
- entry.attributes[label] = tokens[i].value
29
+ type = attr.fetch(:type)
30
+ values = attr_tokens.
31
+ each_slice(type.size).
32
+ map { |slice| Hash[type.zip(slice)] }
33
+
34
+ entry.attributes[label] = values
33
35
  end
34
36
  end
35
37
 
36
38
  entry
37
39
  end
38
40
 
41
+ def attributes_with_tokens
42
+ line_entry_type.map { |attr_entry_type|
43
+ token = tokens.shift
44
+ next unless token
45
+
46
+ if attr_entry_type.is_a?(String)
47
+ [ attr_entry_type, token.value ]
48
+ else
49
+ unless token.is_a?(Tokenizer::BeginArrayToken)
50
+ raise InvalidEntryError, "Unexpected token: #{token.inspect}"
51
+ end
52
+
53
+ hash_tokens = []
54
+ while token = tokens.shift
55
+ break if token.is_a?(Tokenizer::EndArrayToken)
56
+ hash_tokens << token.value
57
+ end
58
+
59
+ [ attr_entry_type, *hash_tokens ]
60
+ end
61
+ }.compact
62
+ end
63
+
39
64
  def build_empty_entry
40
65
  Entry.new(first_token.label)
41
66
  end
42
67
 
43
- def raise_invalid_entry_error
44
- raise InvalidEntryError, "Unknown entry type: #{first_token.label}"
68
+ def line_entry_type
69
+ first_token.entry_type
45
70
  end
46
71
 
47
- def skip_array(tokens, i)
48
- if tokens[i].is_a?(Tokenizer::BeginArrayToken) &&
49
- !tokens[i+1].is_a?(Tokenizer::EndArrayToken)
50
- raise "We currently don't support metadata within entries as we haven't had a need for it yet (the data between {} in #{line})."
51
- end
52
-
53
- tokens.reject! { |token| token.is_a?(Tokenizer::EndArrayToken) }
72
+ def raise_invalid_entry_error
73
+ raise InvalidEntryError, "Unknown entry type: #{first_token.label}"
54
74
  end
55
75
  end
56
76
  end
@@ -42,6 +42,7 @@ module Sie
42
42
  def consume_quoted_value
43
43
  if current_character.quote?
44
44
  @quoted = false
45
+ @consume = false
45
46
  else
46
47
  add_to_current_token current_character
47
48
  end
data/lib/sie/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Sie
2
2
  # For versioning see: http://semver.org/
3
- VERSION = "2.2.0"
3
+ VERSION = "3.0.0"
4
4
  end
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+ require "sie/parser/build_entry"
3
+ require "sie/parser/tokenizer"
4
+
5
+ describe Sie::Parser::BuildEntry, "call" do
6
+ context "with an unexpected token at start of array" do
7
+ it "raises InvalidEntryError" do
8
+ line = '#TRANS 2400 [] -200 20130101 "Foocorp expense"'
9
+ tokens = Sie::Parser::Tokenizer.new(line).tokenize
10
+ first_token = tokens.shift
11
+ build_entry = Sie::Parser::BuildEntry.new(line, first_token, tokens, false)
12
+
13
+ expect { build_entry.call }.to raise_error(Sie::Parser::BuildEntry::InvalidEntryError)
14
+ end
15
+ end
16
+ end
@@ -22,15 +22,35 @@ describe Sie::Document, "#render" do
22
22
  {
23
23
  creditor: false, type: :invoice, number: 1, booked_on: Date.new(2011, 9, 3), description: "Invoice 1",
24
24
  voucher_lines: [
25
- { account_number: 1500, amount: 512.0, booked_on: Date.new(2011, 9, 3), description: "Item 1" },
26
- { account_number: 3100, amount: -512.0, booked_on: Date.new(2011, 9, 3), description: "Item 1" },
25
+ {
26
+ account_number: 1500, amount: 512.0, booked_on: Date.new(2011, 9, 3), description: "Item 1",
27
+ dimensions: { 6 => 1 }
28
+ },
29
+ {
30
+ account_number: 3100, amount: -512.0, booked_on: Date.new(2011, 9, 3), description: "Item 1",
31
+ dimensions: { 6 => 1 }
32
+ },
27
33
  ]
28
34
  },
29
35
  {
30
36
  creditor: true, type: :payment, number: 2, booked_on: Date.new(2012, 8, 31), description: "Payout 1",
31
37
  voucher_lines: [
32
- { account_number: 2400, amount: 256.0, booked_on: Date.new(2012, 8, 31), description: "Payout line 1" },
33
- { account_number: 1970, amount: -256.0, booked_on: Date.new(2012, 8, 31), description: "Payout line 2" },
38
+ {
39
+ account_number: 2400, amount: 256.0, booked_on: Date.new(2012, 8, 31), description: "Payout line 1"
40
+ },
41
+ {
42
+ account_number: 1970, amount: -256.0, booked_on: Date.new(2012, 8, 31), description: "Payout line 2"
43
+ },
44
+ ]
45
+ }
46
+ ]
47
+ }
48
+ let(:dimensions) {
49
+ [
50
+ {
51
+ number: 6, description: "Project",
52
+ objects: [
53
+ { number: 1, description: "Education" }
34
54
  ]
35
55
  }
36
56
  ]
@@ -39,7 +59,7 @@ describe Sie::Document, "#render" do
39
59
  class TestDataSource
40
60
  attr_accessor :program, :program_version, :generated_on, :company_name,
41
61
  :accounts, :balance_account_numbers, :closing_account_numbers,
42
- :vouchers, :financial_years
62
+ :vouchers, :financial_years, :dimensions
43
63
 
44
64
  # vouchers is not part of the expected interface so making it private.
45
65
  #
@@ -73,9 +93,10 @@ describe Sie::Document, "#render" do
73
93
  company_name: "Foocorp",
74
94
  financial_years: financial_years,
75
95
  balance_account_numbers: [ 1500, 2400 ],
76
- closing_account_numbers: [ 3100 ]
96
+ closing_account_numbers: [ 3100 ],
97
+ dimensions: dimensions
77
98
  )
78
- doc = Sie::Document.new(data_source)
99
+ Sie::Document.new(data_source)
79
100
  }
80
101
 
81
102
  let(:sie_file) { Sie::Parser.new.parse(doc.render) }
@@ -106,6 +127,14 @@ describe Sie::Document, "#render" do
106
127
  expect(indexed_entry_attributes("konto", 0)).to eq("kontonr" => "1500", "kontonamn" => "Customer ledger")
107
128
  end
108
129
 
130
+ it "has dimensions" do
131
+ expect(indexed_entry_attributes("dim", 0)).to eq("dimensionsnr" => "6", "namn" => "Project")
132
+ end
133
+
134
+ it "has objects" do
135
+ expect(indexed_entry_attributes("objekt", 0)).to eq("dimensionsnr" => "6", "objektnr" => "1", "objektnamn" => "Education")
136
+ end
137
+
109
138
  it "has balances brought forward (ingående balans)" do
110
139
  expect(indexed_entry_attributes("ib", 0)).to eq("arsnr" => "0", "konto" => "1500", "saldo" => "1600.0")
111
140
  expect(indexed_entry_attributes("ib", 1)).to eq("arsnr" => "0", "konto" => "2400", "saldo" => "2500.0")
@@ -137,11 +166,13 @@ describe Sie::Document, "#render" do
137
166
  )
138
167
  expect(indexed_voucher_entries(0)[0].attributes).to eq(
139
168
  "kontonr" => "1500", "belopp" => "512.0",
140
- "transdat" => "20110903", "transtext" => "Item 1"
169
+ "transdat" => "20110903", "transtext" => "Item 1",
170
+ "objektlista" => [{"dimensionsnr" => "6", "objektnr" => "1"}]
141
171
  )
142
172
  expect(indexed_voucher_entries(0)[1].attributes).to eq(
143
173
  "kontonr" => "3100", "belopp" => "-512.0",
144
- "transdat" => "20110903", "transtext" => "Item 1"
174
+ "transdat" => "20110903", "transtext" => "Item 1",
175
+ "objektlista" => [{"dimensionsnr" => "6", "objektnr" => "1"}]
145
176
  )
146
177
 
147
178
  expect(indexed_entry("ver", 1).attributes).to eq(
@@ -150,11 +181,13 @@ describe Sie::Document, "#render" do
150
181
  )
151
182
  expect(indexed_voucher_entries(1)[0].attributes).to eq(
152
183
  "kontonr" => "2400", "belopp" => "256.0",
153
- "transdat" => "20120831", "transtext" => "Payout line 1"
184
+ "transdat" => "20120831", "transtext" => "Payout line 1",
185
+ "objektlista" => []
154
186
  )
155
187
  expect(indexed_voucher_entries(1)[1].attributes).to eq(
156
188
  "kontonr" => "1970", "belopp" => "-256.0",
157
- "transdat" => "20120831", "transtext" => "Payout line 2"
189
+ "transdat" => "20120831", "transtext" => "Payout line 2",
190
+ "objektlista" => []
158
191
  )
159
192
  end
160
193
 
@@ -3,14 +3,15 @@ require "sie/parser/line_parser"
3
3
 
4
4
  describe Sie::Parser::LineParser, "parse" do
5
5
  it "parses lines from a sie file" do
6
- parser = Sie::Parser::LineParser.new('#TRANS 2400 {} -200 20130101 "Foocorp expense"')
6
+ parser = Sie::Parser::LineParser.new('#TRANS 2400 {"3" "5"} -200 20130101 "Foocorp expense"')
7
7
  entry = parser.parse
8
8
  expect(entry.label).to eq("trans")
9
9
  expect(entry.attributes).to eq({
10
10
  "kontonr" => "2400",
11
11
  "belopp" => "-200",
12
12
  "transdat" => "20130101",
13
- "transtext" => "Foocorp expense"
13
+ "transtext" => "Foocorp expense",
14
+ "objektlista" => [{"dimensionsnr" => "3", "objektnr" => "5"}],
14
15
  })
15
16
  end
16
17
 
@@ -33,9 +34,4 @@ describe Sie::Parser::LineParser, "parse" do
33
34
  end
34
35
  end
35
36
  end
36
-
37
- it "fails if you have non empty metadata arrays until there is a need to support that" do
38
- parser = Sie::Parser::LineParser.new('#TRANS 2400 { 1 "2" } -200 20130101 "Foocorp expense"')
39
- expect(-> { parser.parse }).to raise_error(/don't support metadata/)
40
- end
41
37
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sie
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Barsoom AB
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-12 00:00:00.000000000 Z
11
+ date: 2015-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: attr_extras
@@ -117,6 +117,7 @@ files:
117
117
  - spec/fixtures/sie_file_with_unknown_entries.se
118
118
  - spec/integration/parser_spec.rb
119
119
  - spec/spec_helper.rb
120
+ - spec/unit/build_entry_spec.rb
120
121
  - spec/unit/document/renderer_spec.rb
121
122
  - spec/unit/document/voucher_series_spec.rb
122
123
  - spec/unit/document_spec.rb
@@ -152,6 +153,7 @@ test_files:
152
153
  - spec/fixtures/sie_file_with_unknown_entries.se
153
154
  - spec/integration/parser_spec.rb
154
155
  - spec/spec_helper.rb
156
+ - spec/unit/build_entry_spec.rb
155
157
  - spec/unit/document/renderer_spec.rb
156
158
  - spec/unit/document/voucher_series_spec.rb
157
159
  - spec/unit/document_spec.rb