sie-analysis 1.0.0 → 1.1.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 +4 -4
- data/README.md +10 -2
- data/lib/sie_analysis/analysis.rb +25 -8
- data/lib/sie_analysis/budgets.rb +1 -1
- data/lib/sie_analysis/dimension_statements.rb +3 -3
- data/lib/sie_analysis/parser.rb +15 -4
- data/lib/sie_analysis/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 15ee746d937f73cb9a8be5d8ac35133cfaca53d919be9df549ae073b6ef623be
|
|
4
|
+
data.tar.gz: b12af56b12387792f0f68f40cb400c01e7f63b9c98b107359209225a1e7969fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de5892b81dfad72c9d895cb1a831916750bf7a406099673f91519735778be9f41f418917fa996c086bf7d7d9711aec94c92c6f0128cf65b88d059d1ea06aa49c
|
|
7
|
+
data.tar.gz: 68bf1df9481362cbd6787317016d06373ebf4e2b8e13c508bec62eb1c176f603de1916912ac58548ac32cdfaeb70755374eaa0bb00213776bcd5383645d21897
|
data/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Requires Ruby 3.2 or newer. From a local checkout, build and install the gem:
|
|
|
11
11
|
|
|
12
12
|
```sh
|
|
13
13
|
gem build sie-analysis.gemspec
|
|
14
|
-
gem install ./sie-analysis-1.
|
|
14
|
+
gem install ./sie-analysis-1.1.0.gem
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Load an export and create an analyzer:
|
|
@@ -77,7 +77,15 @@ report[:income_statement][:groups].map { |group| group.slice(:code, :label, :amo
|
|
|
77
77
|
|
|
78
78
|
Use `dimension: :cost_center` for cost centers. `account_movements` accepts the
|
|
79
79
|
same dimensions, with `dimension_code: "ABC"` to select an object.
|
|
80
|
-
`analysis.available_dimensions` lists
|
|
80
|
+
`analysis.available_dimensions` lists standard names as symbols and other dimensions
|
|
81
|
+
as integers. Supported names are `:cost_center` (1), `:cost_bearer` (2), `:project` (6),
|
|
82
|
+
`:employee` (7), `:customer` (8), `:supplier` (9), and `:invoice` (10).
|
|
83
|
+
Any dimension can also be selected by its SIE number, for example `dimension: 20`.
|
|
84
|
+
Declared dimensions and dimensions referenced in objects, balances, budgets or
|
|
85
|
+
transactions are included, even without monthly balances.
|
|
86
|
+
`document.dimensions` maps declared numbers to names, e.g. `{ 20 => "Department" }`;
|
|
87
|
+
`document.vouchers.first.lines.first.objects` contains pairs such as `[[20, "A"]]`.
|
|
88
|
+
The export must contain the relevant object balances;
|
|
81
89
|
balance-sheet totals are omitted for dimension reports.
|
|
82
90
|
|
|
83
91
|
## Inspect the postings on an account
|
|
@@ -15,7 +15,12 @@ module SieAnalysis
|
|
|
15
15
|
CURRENCY = "SEK"
|
|
16
16
|
DIMENSIONS = {
|
|
17
17
|
cost_center: 1,
|
|
18
|
-
|
|
18
|
+
cost_bearer: 2,
|
|
19
|
+
project: 6,
|
|
20
|
+
employee: 7,
|
|
21
|
+
customer: 8,
|
|
22
|
+
supplier: 9,
|
|
23
|
+
invoice: 10
|
|
19
24
|
}.freeze
|
|
20
25
|
|
|
21
26
|
# @param document [Parser::Parsed] a decoded and parsed SIE export
|
|
@@ -26,7 +31,7 @@ module SieAnalysis
|
|
|
26
31
|
# Summarizes all calendar months touched by the inclusive date range.
|
|
27
32
|
# @param from [Date] first date
|
|
28
33
|
# @param to [Date] last date
|
|
29
|
-
# @param dimension [Symbol, nil]
|
|
34
|
+
# @param dimension [Symbol, Integer, nil] standard dimension name or SIE dimension number
|
|
30
35
|
# @return [Hash] income statement, balance sheet, budget, monthly totals and notes
|
|
31
36
|
def overview(from:, to:, dimension: nil)
|
|
32
37
|
start_date, end_date = from, to
|
|
@@ -57,7 +62,7 @@ module SieAnalysis
|
|
|
57
62
|
# @param month [String] YYYY-MM
|
|
58
63
|
# @param group_code [String, nil] report group, e.g. "revenue"
|
|
59
64
|
# @param account_number [Integer, nil] BAS account number
|
|
60
|
-
# @param dimension [Symbol, nil]
|
|
65
|
+
# @param dimension [Symbol, Integer, nil] standard dimension name or SIE dimension number
|
|
61
66
|
# @param dimension_code [String, nil] object code; requires dimension
|
|
62
67
|
# @return [Array<Hash>] account_number, account_name, movement and group/object fields
|
|
63
68
|
def account_movements(month:, group_code: nil, account_number: nil, dimension: nil, dimension_code: nil)
|
|
@@ -111,12 +116,19 @@ module SieAnalysis
|
|
|
111
116
|
end
|
|
112
117
|
end
|
|
113
118
|
|
|
114
|
-
#
|
|
119
|
+
# Includes declared dimensions and dimensions referenced by objects, balances or vouchers.
|
|
120
|
+
# @return [Array<Symbol, Integer>] standard names and other SIE dimension numbers
|
|
115
121
|
def available_dimensions
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
numbers = @parsed.dimensions.keys + @parsed.objects.keys.map(&:first)
|
|
123
|
+
[@parsed.period_changes, @parsed.budgets].each do |periods|
|
|
124
|
+
periods.each_value do |rows|
|
|
125
|
+
numbers.concat(rows.keys.filter_map { |key| key[1] if key.is_a?(Array) })
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
@parsed.vouchers.each do |voucher|
|
|
129
|
+
voucher.lines.each { |line| numbers.concat(line.objects.map(&:first)) }
|
|
130
|
+
end
|
|
131
|
+
numbers.uniq.sort.map { |number| DIMENSIONS.key(number) || number }
|
|
120
132
|
end
|
|
121
133
|
|
|
122
134
|
private
|
|
@@ -200,11 +212,16 @@ module SieAnalysis
|
|
|
200
212
|
|
|
201
213
|
def normalize_dimension(dimension)
|
|
202
214
|
return if dimension.nil?
|
|
215
|
+
return DIMENSIONS.key(dimension) || dimension if dimension.is_a?(Integer) && dimension.positive?
|
|
203
216
|
raise ArgumentError, "Unsupported dimension" unless DIMENSIONS.key?(dimension)
|
|
204
217
|
|
|
205
218
|
dimension
|
|
206
219
|
end
|
|
207
220
|
|
|
221
|
+
def dimension_number(dimension)
|
|
222
|
+
dimension.is_a?(Integer) ? dimension : DIMENSIONS.fetch(dimension)
|
|
223
|
+
end
|
|
224
|
+
|
|
208
225
|
def normalize_year_month(value)
|
|
209
226
|
text = value.to_s
|
|
210
227
|
raise ArgumentError, "month must be YYYY-MM" unless text.match?(/\A\d{4}-\d{2}\z/)
|
data/lib/sie_analysis/budgets.rb
CHANGED
|
@@ -20,7 +20,7 @@ module SieAnalysis
|
|
|
20
20
|
|
|
21
21
|
def budget_row(month, key, raw, dimension)
|
|
22
22
|
account, dim, code = dimension_parts(key)
|
|
23
|
-
return unless dim == (dimension &&
|
|
23
|
+
return unless dim == (dimension && dimension_number(dimension))
|
|
24
24
|
|
|
25
25
|
group = Groups.for_account(account)
|
|
26
26
|
return unless income_group?(group)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SieAnalysis
|
|
4
|
-
#
|
|
4
|
+
# Income and account-row builders for any SIE dimension.
|
|
5
5
|
module DimensionStatements
|
|
6
6
|
private
|
|
7
7
|
|
|
8
8
|
def dimension_income_groups(months, dimension)
|
|
9
|
-
dim_no =
|
|
9
|
+
dim_no = dimension_number(dimension)
|
|
10
10
|
totals = accumulate_dimension_totals(months, dim_no)
|
|
11
11
|
groups = dimension_group_rows(totals, dim_no)
|
|
12
12
|
revenue = totals.values.sum { |values| values[:revenue] }
|
|
@@ -56,7 +56,7 @@ module SieAnalysis
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def dimension_account_rows(month, dimension)
|
|
59
|
-
dim_no =
|
|
59
|
+
dim_no = dimension_number(dimension)
|
|
60
60
|
rows = []
|
|
61
61
|
(@parsed.period_changes[month] || {}).each do |key, raw|
|
|
62
62
|
account, dim, code = dimension_parts(key)
|
data/lib/sie_analysis/parser.rb
CHANGED
|
@@ -7,13 +7,15 @@ module SieAnalysis
|
|
|
7
7
|
class Parser
|
|
8
8
|
Record = Data.define(:tag, :fields)
|
|
9
9
|
Voucher = Data.define(:series, :number, :date, :description, :lines)
|
|
10
|
-
|
|
10
|
+
# Voucher line with [dimension number, object code] references.
|
|
11
|
+
Line = Data.define(:account, :amount, :objects)
|
|
11
12
|
|
|
12
13
|
# Parsed ledger: monetary values are BigDecimal; voucher dates are Date.
|
|
13
14
|
# Period keys are YYYYMM strings; object keys are [dimension number, code].
|
|
15
|
+
# Dimensions maps declared dimension numbers to their exported names.
|
|
14
16
|
Parsed = Data.define(
|
|
15
17
|
:accounts, :objects, :opening_balances, :closing_balances, :results, :period_changes,
|
|
16
|
-
:financial_years, :vouchers, :budgets, :sie_type
|
|
18
|
+
:financial_years, :vouchers, :budgets, :sie_type, :dimensions
|
|
17
19
|
)
|
|
18
20
|
|
|
19
21
|
# @param text [String] export text already decoded to UTF-8
|
|
@@ -28,6 +30,7 @@ module SieAnalysis
|
|
|
28
30
|
@text = text.to_s
|
|
29
31
|
@accounts = {}
|
|
30
32
|
@objects = {}
|
|
33
|
+
@dimensions = {}
|
|
31
34
|
@opening = Hash.new(BigDecimal("0"))
|
|
32
35
|
@closing = Hash.new(BigDecimal("0"))
|
|
33
36
|
@results = Hash.new(BigDecimal("0"))
|
|
@@ -50,7 +53,7 @@ module SieAnalysis
|
|
|
50
53
|
Parsed.new(
|
|
51
54
|
accounts: @accounts, objects: @objects, opening_balances: @opening, closing_balances: @closing,
|
|
52
55
|
results: @results, period_changes: @periods, financial_years: @years, vouchers: @vouchers,
|
|
53
|
-
budgets: @budgets, sie_type: @sie_type
|
|
56
|
+
budgets: @budgets, sie_type: @sie_type, dimensions: @dimensions
|
|
54
57
|
)
|
|
55
58
|
rescue ParseError
|
|
56
59
|
raise
|
|
@@ -79,6 +82,7 @@ module SieAnalysis
|
|
|
79
82
|
@sie_type = Integer(record.fields.fetch(0))
|
|
80
83
|
when "KONTO" then apply_account(record.fields)
|
|
81
84
|
when "OBJEKT" then apply_object(record.fields)
|
|
85
|
+
when "DIM", "UNDERDIM" then apply_dimension(record.fields)
|
|
82
86
|
when "RAR" then apply_year(record.fields)
|
|
83
87
|
when "IB" then apply_balance!(@opening, record.fields)
|
|
84
88
|
when "UB" then apply_balance!(@closing, record.fields)
|
|
@@ -102,6 +106,13 @@ module SieAnalysis
|
|
|
102
106
|
@objects[[dim, code]] = name if dim && !code.empty?
|
|
103
107
|
end
|
|
104
108
|
|
|
109
|
+
def apply_dimension(fields)
|
|
110
|
+
number = integer_field(fields[0])
|
|
111
|
+
raise ArgumentError unless number&.positive?
|
|
112
|
+
|
|
113
|
+
@dimensions[number] = unquote(fields.fetch(1))
|
|
114
|
+
end
|
|
115
|
+
|
|
105
116
|
def apply_year(fields)
|
|
106
117
|
year_no = integer_field(fields[0])
|
|
107
118
|
first, last = sie_date(fields[1]), sie_date(fields[2])
|
|
@@ -189,7 +200,7 @@ module SieAnalysis
|
|
|
189
200
|
|
|
190
201
|
raise ArgumentError if object_field.start_with?("{") && !valid_budget_objects?(object_field)
|
|
191
202
|
|
|
192
|
-
@current_voucher[:lines] << Line.new(account:, amount:)
|
|
203
|
+
@current_voucher[:lines] << Line.new(account:, amount:, objects: object_refs_from(object_field))
|
|
193
204
|
end
|
|
194
205
|
|
|
195
206
|
def finish_voucher!
|
data/lib/sie_analysis/version.rb
CHANGED