sie-analysis 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5b974bd28fe57c5e58938fa521b1127915fea8febdd5064dd9f8e9a1efbe21c5
4
+ data.tar.gz: a3ab850a3005f94f448fe53702afe6f4c182cf6e00c4b908275d54285cb94d20
5
+ SHA512:
6
+ metadata.gz: 9c6d582185da464d659754781d4089e5b96f7d1691f1afb7ff76f58cceabae00d9bcf4bed6e3634a28aa0b1c3a386f7c2fe320242c807d8bcd42c72e729ce2ea
7
+ data.tar.gz: 83063537fa8c773ba8c449a2cc30539f0cc4856500e7707694baaac56c7265966a5a593b85fe1a87c5478ee45026cdb5bee36cb711af6f8c51abe7f5ae0c3d0d
data/README.md ADDED
@@ -0,0 +1,185 @@
1
+ ![SIE Analysis — making cents of your ledgers](assets/header-vector.svg)
2
+
3
+ # SIE Analysis
4
+
5
+ Turn Swedish SIE exports into income statements, account movements, budget
6
+ comparisons and voucher details in Ruby.
7
+
8
+ ## Get started
9
+
10
+ Requires Ruby 3.2 or newer. From a local checkout, build and install the gem:
11
+
12
+ ```sh
13
+ gem build sie-analysis.gemspec
14
+ gem install ./sie-analysis-0.1.1.gem
15
+ ```
16
+
17
+ Load an export and create an analyzer:
18
+
19
+ ```ruby
20
+ require "sie_analysis"
21
+
22
+ text = File.read("export.se", encoding: "UTF-8")
23
+ # For a file declaring #FORMAT PC8 instead:
24
+ # text = File.read("export.se", encoding: "IBM437:UTF-8")
25
+ parsed = SieAnalysis::Parser.parse(text)
26
+ analysis = SieAnalysis::Analysis.new(parsed)
27
+ ```
28
+
29
+ All monetary results are `BigDecimal`; date boundaries and voucher dates are
30
+ `Date` objects. Examples below abbreviate longer results with `...`.
31
+
32
+ ## Income statement and monthly trends
33
+
34
+ ```ruby
35
+ parsed = SieAnalysis::Parser.parse(File.read("spec/fixtures/sie_type2_sample.se"))
36
+ analysis = SieAnalysis::Analysis.new(parsed)
37
+ report = analysis.overview(
38
+ from: Date.new(2024, 1, 1), to: Date.new(2024, 3, 31)
39
+ )
40
+ report[:income_statement].slice(:revenue, :expenses, :result)
41
+ # => { revenue: 0.3e6, expenses: 0.21e6, result: 0.9e5 }
42
+
43
+ report[:monthly_totals].map { |month| month.slice(:month, :revenue) }
44
+ # => [{ month: "2024-01", revenue: 0.1e6 }, ...]
45
+ ```
46
+
47
+ The report also includes account groups, a balance sheet, cash and bank,
48
+ profit margin, budget information and explanatory notes.
49
+
50
+ ## Find the accounts behind a total
51
+
52
+ ```ruby
53
+ analysis.account_movements(month: "2024-01", account_number: 3010)
54
+ # => [{ account_number: 3010, account_name: "Forsaljning", group_code: "revenue",
55
+ # movement: 0.1e6, dimension_code: nil, dimension_label: nil }]
56
+
57
+ analysis.account_summary(3010, month: "2024-03")
58
+ # => { account_number: 3010, opening_balance: 0.22e6,
59
+ # movement: 0.8e5, closing_balance: 0.3e6, ... }
60
+ ```
61
+
62
+ Omit `account_number` to list all non-zero account movements, or pass
63
+ `group_code: "revenue"` to select a report group.
64
+
65
+ ## Break down results by project or cost center
66
+
67
+ ```ruby
68
+ parsed = SieAnalysis::Parser.parse(File.read("spec/fixtures/sie_type3_sample.se"))
69
+ analysis = SieAnalysis::Analysis.new(parsed)
70
+ report = analysis.overview(
71
+ from: Date.new(2024, 1, 1), to: Date.new(2024, 1, 31),
72
+ dimension: :project
73
+ )
74
+ report[:income_statement][:groups].map { |group| group.slice(:code, :label, :amount) }
75
+ # => [{ code: "ABC", label: "Projekt ABC", amount: 0.15e5 }]
76
+ ```
77
+
78
+ Use `dimension: :cost_center` for cost centers. `account_movements` accepts the
79
+ same dimensions, with `dimension_code: "ABC"` to select an object.
80
+ `analysis.available_dimensions` lists the available symbols. The export must contain the relevant object balances;
81
+ balance-sheet totals are omitted for dimension reports.
82
+
83
+ ## Inspect the postings on an account
84
+
85
+ SIE 4 exports provide voucher details and single-day account checks:
86
+
87
+ ```ruby
88
+ parsed = SieAnalysis::Parser.parse(File.read("spec/fixtures/sie_type4_sample.se"))
89
+ analysis = SieAnalysis::Analysis.new(parsed)
90
+ analysis.account_summary(3010, on: Date.new(2024, 3, 15))
91
+ # => { opening_balance: 0.22e6, movement: 0.5e3,
92
+ # closing_balance: 0.2205e6, ... }
93
+
94
+ analysis.vouchers_on_account(
95
+ 3010,
96
+ from: Date.new(2024, 3, 15), to: Date.new(2024, 3, 15)
97
+ )
98
+ # => [{ voucher_series: "", voucher_number: 5, date: Date.new(2024, 3, 15),
99
+ # description: "March sale", movement: 0.5e3 }]
100
+ ```
101
+
102
+ Each returned amount is the net movement on the selected account in that
103
+ voucher. The method returns all matching vouchers; apply sorting or pagination
104
+ in your script if needed.
105
+
106
+ ## Compare actuals with a budget
107
+
108
+ ```ruby
109
+ parsed = SieAnalysis::Parser.parse(File.read("spec/fixtures/sie_budget_sample.se"))
110
+ analysis = SieAnalysis::Analysis.new(parsed)
111
+ report = analysis.overview(
112
+ from: Date.new(2026, 9, 1), to: Date.new(2026, 9, 30)
113
+ )
114
+ report[:budget][:rows].first.slice(:account_number, :budget, :actual, :variance)
115
+ # => { account_number: 3001, budget: 0.15416e5, actual: 0.2e5, variance: 0.4584e4 }
116
+ ```
117
+
118
+ Budgets come from `PBUDGET` records. Variance is actual minus budget, not a
119
+ judgment of whether the difference is favorable. Missing budgets return
120
+ `status: "unavailable"`. All matching budget rows are returned, including explicitly exported zero budgets.
121
+
122
+ ## Calculation behavior
123
+
124
+ - Overviews use monthly `PSALDO` changes. Date arguments select months; they do
125
+ not filter individual postings. If an export was already date-filtered, its
126
+ supplied balances are used as-is.
127
+ - Supply one financial year per analysis and validate the requested dates
128
+ against that export. Years are not fetched or merged automatically.
129
+ - Income groups use BAS account ranges 3000–8999, including account 8999.
130
+ Amounts use `BigDecimal` arithmetic and are rounded to two decimals using
131
+ half-up rounding. Format them for display with `amount.to_s("F")`.
132
+ - Reports currently label currency as SEK. Only use SEK exports.
133
+ - Daily checks use voucher dates and require the preceding movements needed
134
+ to calculate opening balances. They are not a full reconciliation of the
135
+ export's transactions against its balances.
136
+ - Parsing failures raise `SieAnalysis::ParseError`. Invalid analysis arguments
137
+ raise `ArgumentError`. Daily checks and voucher lists require SIE 4.
138
+ Parsing validates the export type, financial-year headers and recognized
139
+ financial rows; it is not full SIE validation.
140
+
141
+ ## Development
142
+
143
+ From the repository directory:
144
+
145
+ ```sh
146
+ bundle install
147
+ bundle exec rake # Run the complete test suite
148
+ bundle exec rspec spec/analysis_spec.rb
149
+ bundle exec irb -Ilib -rsie_analysis # Explore the library interactively
150
+ gem build sie-analysis.gemspec # Build the distributable gem
151
+ ```
152
+
153
+ Implementation lives in `lib/sie_analysis/`, examples of export data in
154
+ `spec/fixtures/`, and tests in `spec/`. Add a small synthetic fixture and a
155
+ regression test when changing parsing or calculations.
156
+
157
+ To use your checkout from another Ruby project during development:
158
+
159
+ ```ruby
160
+ # Gemfile in the consuming project
161
+ gem "sie-analysis", path: "../sie-analysis"
162
+ ```
163
+
164
+ Run `bundle install` there after adding the dependency.
165
+
166
+ ## Releases
167
+
168
+ The version lives in `lib/sie_analysis/version.rb`. Update it and run
169
+ `bundle install` to refresh the lockfile before committing a release.
170
+ Push a matching tag, for example `v0.1.0`, to publish that version to RubyGems.
171
+ The release workflow runs the test matrix first and rejects a tag that does
172
+ not match the gem version. Pushing commits or opening/updating a pull request
173
+ does not publish a gem.
174
+
175
+ Before the first release, configure a RubyGems
176
+ [pending trusted publisher](https://rubygems.org/profile/oidc/pending_trusted_publishers)
177
+ with these values:
178
+
179
+ - Gem: `sie-analysis`
180
+ - Repository owner: `standout`
181
+ - Repository: `sie-analysis`
182
+ - Workflow filename: `release.yml`
183
+ - Environment: `release`
184
+
185
+ Publishing uses GitHub OIDC; no RubyGems API key is stored in this repository.