hledger-forecast 3.1.0 → 3.2.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/.github/workflows/release.yml +28 -1
- data/CHANGELOG.md +8 -0
- data/README.md +15 -2
- data/lib/hledger_forecast/calculator.rb +11 -0
- data/lib/hledger_forecast/transaction.rb +9 -2
- data/lib/hledger_forecast/version.rb +1 -1
- data/spec/calculator_spec.rb +18 -0
- data/spec/tags_spec.rb +26 -0
- metadata +3 -4
- data/.github/workflows/publish_ruby_gem.yml +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5acfd1bbab287a58bfe5ce7237576535b6b927d034da9e3651a4c240653e0f33
|
|
4
|
+
data.tar.gz: 23eb8d2646996b422473440b8f90797215a7390f4d6e2862b470bd361b6d55d5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 62a027f56e0faf7641769b13928ed61c91e3851fe6b1623df00efd233ec244be03c57d7b94ea2605f01580ba18e0a916807a59c6ef848118d9b80fa9a3e8be28
|
|
7
|
+
data.tar.gz: 470b89a138eff0a2f5da24d4a4fe31ffd7e9584646094665020d19a40a9303877073bfcbc95eb35eae1498149fbfa96b899bd44c5ee4eab4225f26acec9b2e11
|
|
@@ -13,9 +13,36 @@ jobs:
|
|
|
13
13
|
contents: write
|
|
14
14
|
pull-requests: write
|
|
15
15
|
|
|
16
|
+
outputs:
|
|
17
|
+
release_created: ${{ steps.release.outputs.release_created }}
|
|
18
|
+
|
|
16
19
|
steps:
|
|
17
|
-
- name: Release
|
|
20
|
+
- name: Release
|
|
21
|
+
id: release
|
|
18
22
|
uses: googleapis/release-please-action@v4
|
|
19
23
|
with:
|
|
20
24
|
release-type: ruby
|
|
21
25
|
|
|
26
|
+
publish:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
needs: release
|
|
29
|
+
if: needs.release.outputs.release_created == 'true'
|
|
30
|
+
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
|
|
34
|
+
- name: Set up Ruby
|
|
35
|
+
uses: ruby/setup-ruby@v1
|
|
36
|
+
with:
|
|
37
|
+
ruby-version: "3.3"
|
|
38
|
+
|
|
39
|
+
- name: Publish to RubyGems
|
|
40
|
+
run: |
|
|
41
|
+
mkdir -p $HOME/.gem
|
|
42
|
+
touch $HOME/.gem/credentials
|
|
43
|
+
chmod 0600 $HOME/.gem/credentials
|
|
44
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
|
45
|
+
gem build *.gemspec
|
|
46
|
+
gem push *.gem
|
|
47
|
+
env:
|
|
48
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.2.0](https://github.com/olimorris/hledger-forecast/compare/v3.1.0...v3.2.0) (2026-04-13)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* can exclude tags in summarizer ([#16](https://github.com/olimorris/hledger-forecast/issues/16)) ([faee83e](https://github.com/olimorris/hledger-forecast/commit/faee83ee8eb554d146177832c307b87f5ecd4616))
|
|
9
|
+
* from date can have calculations ([#18](https://github.com/olimorris/hledger-forecast/issues/18)) ([e62a693](https://github.com/olimorris/hledger-forecast/commit/e62a69385bf6c34f2d446593d5046fe22a1e7f5f))
|
|
10
|
+
|
|
3
11
|
## [3.1.0](https://github.com/olimorris/hledger-forecast/compare/v3.0.0...v3.1.0) (2026-04-02)
|
|
4
12
|
|
|
5
13
|
|
data/README.md
CHANGED
|
@@ -147,7 +147,7 @@ The CSV file should have a header row with these columns:
|
|
|
147
147
|
| `type` | string | yes | One of: `monthly`, `quarterly`, `half-yearly`, `yearly`, `once`, `custom` |
|
|
148
148
|
| `frequency` | string | `custom` only | Repeating frequency, using hledger's [periodic rule syntax](https://hledger.org/dev/hledger.html#periodic-transactions) |
|
|
149
149
|
| `account` | string | yes | The account the transaction applies to, e.g. `Assets:Bank` |
|
|
150
|
-
| `from` | date | yes | Start date, e.g. `01/03/2023` |
|
|
150
|
+
| `from` | date | yes | Start date, e.g. `01/03/2023`. Supports `=` prefix for calculated values, e.g. `=01/03/2023+(5*12)` |
|
|
151
151
|
| `to` | date | no | End date, e.g. `01/01/2025`. Supports `+` prefix for calculated values, e.g. `+12` for 12 months |
|
|
152
152
|
| `description` | string | yes | A description of the transaction |
|
|
153
153
|
| `category` | string | yes | The category account, e.g. `Expenses:Food` |
|
|
@@ -189,7 +189,17 @@ monthly,,Assets:Bank,01/03/2023,,New Kitchen,Expenses:House,=5000/24,,
|
|
|
189
189
|
|
|
190
190
|
### Calculated dates
|
|
191
191
|
|
|
192
|
-
|
|
192
|
+
Both `from` and `to` support calculated values.
|
|
193
|
+
|
|
194
|
+
**`from` column** — prefix with `=` to compute a start date. The formula is `=BASE_DATE+OFFSET` where the offset is evaluated as months:
|
|
195
|
+
|
|
196
|
+
```csv
|
|
197
|
+
monthly,,Assets:Bank,=01/09/2025+(5*12),,Salary,Income:Salary,-3500,,
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
That sets the start date to 60 months (5 years) after `01/09/2025`, giving `01/09/2030`.
|
|
201
|
+
|
|
202
|
+
**`to` column** — use `+` followed by a number to mean "N months from the `from` date":
|
|
193
203
|
|
|
194
204
|
```csv
|
|
195
205
|
monthly,,Assets:Bank,01/03/2026,+12,Holiday,Expenses:Holiday,125,,
|
|
@@ -235,6 +245,9 @@ hledger-forecast summarize -f forecast.csv --tags=essential
|
|
|
235
245
|
|
|
236
246
|
# Multiple tags use OR logic — matches any
|
|
237
247
|
hledger-forecast summarize -f forecast.csv --tags=fixed,living
|
|
248
|
+
|
|
249
|
+
# Can also exclude tags with a `-` prefix
|
|
250
|
+
hledger-forecast summarize -f forecast.csv --tags=fixed,-essential
|
|
238
251
|
```
|
|
239
252
|
|
|
240
253
|
**Querying in hledger** - because the tags are native hledger format, you can query them directly:
|
|
@@ -8,6 +8,17 @@ module HledgerForecast
|
|
|
8
8
|
@calc.evaluate(amount.slice(1..-1))
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def self.evaluate_from_date(value)
|
|
12
|
+
value = value.to_s
|
|
13
|
+
return Date.parse(value) unless value.start_with?("=")
|
|
14
|
+
|
|
15
|
+
date_str, offset_expr = value[1..].split("+", 2)
|
|
16
|
+
date = Date.parse(date_str)
|
|
17
|
+
return date unless offset_expr
|
|
18
|
+
|
|
19
|
+
date >> @calc.evaluate(offset_expr).to_i
|
|
20
|
+
end
|
|
21
|
+
|
|
11
22
|
def self.evaluate_date(from, to)
|
|
12
23
|
return (from >> to) - 1 if to.is_a?(Numeric)
|
|
13
24
|
return Date.parse(to) unless to.start_with?("=") || to.start_with?("+")
|
|
@@ -24,7 +24,7 @@ module HledgerForecast
|
|
|
24
24
|
keyword_init: true
|
|
25
25
|
) do
|
|
26
26
|
def self.from_row(row)
|
|
27
|
-
from =
|
|
27
|
+
from = Calculator.evaluate_from_date(row[:from])
|
|
28
28
|
new(
|
|
29
29
|
type: row[:type],
|
|
30
30
|
frequency: row[:frequency],
|
|
@@ -42,7 +42,14 @@ module HledgerForecast
|
|
|
42
42
|
|
|
43
43
|
def matches_tags?(filter_tags)
|
|
44
44
|
return true if filter_tags.nil? || filter_tags.empty?
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
exclude_tags = filter_tags.select { |t| t.start_with?("-") }.map { |t| t[1..] }
|
|
47
|
+
include_tags = filter_tags.reject { |t| t.start_with?("-") }
|
|
48
|
+
|
|
49
|
+
return false if exclude_tags.any? && (tags & exclude_tags).any?
|
|
50
|
+
return (tags & include_tags).any? if include_tags.any?
|
|
51
|
+
|
|
52
|
+
true
|
|
46
53
|
end
|
|
47
54
|
|
|
48
55
|
def annualised_amount
|
data/spec/calculator_spec.rb
CHANGED
|
@@ -23,6 +23,24 @@ RSpec.describe HledgerForecast::Calculator do
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
describe ".evaluate_from_date" do
|
|
27
|
+
it "parses a plain date string" do
|
|
28
|
+
expect(described_class.evaluate_from_date("01/09/2022")).to(eq(Date.parse("2022-09-01")))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "evaluates a date with a month offset" do
|
|
32
|
+
expect(described_class.evaluate_from_date("=01/09/2022+6")).to(eq(Date.parse("2023-03-01")))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "evaluates a date with a multiplied offset" do
|
|
36
|
+
expect(described_class.evaluate_from_date("=01/09/2022+(5*12)")).to(eq(Date.parse("2027-09-01")))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "evaluates a formula with no offset" do
|
|
40
|
+
expect(described_class.evaluate_from_date("=01/09/2022")).to(eq(Date.parse("2022-09-01")))
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
26
44
|
describe ".evaluate_date" do
|
|
27
45
|
let(:from) { Date.parse("2023-03-01") }
|
|
28
46
|
|
data/spec/tags_spec.rb
CHANGED
|
@@ -72,6 +72,32 @@ RSpec.describe "tags" do
|
|
|
72
72
|
expect(descriptions).to(eq(["Food", "Netflix"]))
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
it "excludes transactions by tag with - prefix" do
|
|
76
|
+
result = HledgerForecast::Summarizer.summarize(config, {tags: ["-fixed"]})
|
|
77
|
+
descriptions = result[:output].map { |r| r[:description] }
|
|
78
|
+
|
|
79
|
+
expect(descriptions).to(eq(["Food", "Netflix"]))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "excludes tags in generator output" do
|
|
83
|
+
expected = <<~JOURNAL
|
|
84
|
+
~ monthly from 2023-03-01 * Food, Netflix
|
|
85
|
+
Expenses:Food £500.00; living:, essential:
|
|
86
|
+
Expenses:Subscriptions £15.00 ; living:
|
|
87
|
+
Assets:Bank
|
|
88
|
+
|
|
89
|
+
JOURNAL
|
|
90
|
+
|
|
91
|
+
expect(HledgerForecast::Generator.generate(config, {tags: ["-fixed"]})).to(eq(expected))
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "combines include and exclude tags" do
|
|
95
|
+
result = HledgerForecast::Summarizer.summarize(config, {tags: ["essential", "-fixed"]})
|
|
96
|
+
descriptions = result[:output].map { |r| r[:description] }
|
|
97
|
+
|
|
98
|
+
expect(descriptions).to(eq(["Food"]))
|
|
99
|
+
end
|
|
100
|
+
|
|
75
101
|
it "raises an error when --tags is used on a CSV without a tag column" do
|
|
76
102
|
csv_without_tag_column = <<~CSV
|
|
77
103
|
type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hledger-forecast
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oli Morris
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: abbrev
|
|
@@ -132,7 +132,6 @@ extensions: []
|
|
|
132
132
|
extra_rdoc_files: []
|
|
133
133
|
files:
|
|
134
134
|
- ".github/workflows/ci.yml"
|
|
135
|
-
- ".github/workflows/publish_ruby_gem.yml"
|
|
136
135
|
- ".github/workflows/release.yml"
|
|
137
136
|
- ".gitignore"
|
|
138
137
|
- ".mise.toml"
|
|
@@ -201,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
201
200
|
- !ruby/object:Gem::Version
|
|
202
201
|
version: '0'
|
|
203
202
|
requirements: []
|
|
204
|
-
rubygems_version: 3.
|
|
203
|
+
rubygems_version: 3.5.22
|
|
205
204
|
signing_key:
|
|
206
205
|
specification_version: 4
|
|
207
206
|
summary: Generate hledger journal entries and income statements from a CSV forecast
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
name: Publish Ruby Gem
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- 'v*'
|
|
7
|
-
workflow_dispatch:
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
release:
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
|
|
13
|
-
steps:
|
|
14
|
-
- uses: actions/checkout@v4
|
|
15
|
-
- name: Publish Gem 💎️
|
|
16
|
-
run: |
|
|
17
|
-
mkdir -p $HOME/.gem
|
|
18
|
-
touch $HOME/.gem/credentials
|
|
19
|
-
chmod 0600 $HOME/.gem/credentials
|
|
20
|
-
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
|
21
|
-
gem build *.gemspec
|
|
22
|
-
gem push *.gem
|
|
23
|
-
env:
|
|
24
|
-
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|