sie 2.2.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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