hledger-forecast 0.3.0 → 0.4.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 +23 -4
- data/example.journal +6 -1
- data/example.yml +3 -0
- data/hledger-forecast.gemspec +1 -0
- data/lib/hledger_forecast/generator.rb +14 -5
- data/lib/hledger_forecast/version.rb +1 -1
- data/lib/hledger_forecast.rb +1 -0
- data/spec/computed_amounts_spec.rb +35 -0
- metadata +18 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 85bd96722d50a8d5b2c8e8cbb9f973d88832827f74226d3b78d5f511b96265bf
         | 
| 4 | 
            +
              data.tar.gz: b69f6698e5c7294cd2b62c8672887044a416d0ed6624803293acd9246a84fa14
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 2e3c4e2a1ef57546778525c37fa4b53854a10244f3e509c3bd48d8c3fcb49c2edc892c84f221f7980c56687ec4766f5379939ea82a06a06b238a8aa3f1a1ed8b
         | 
| 7 | 
            +
              data.tar.gz: 4fd49fc32b5fd5aa17e24ac166f384352a5cb85a72f57cb3aa774b8837c62c94439412252ba7164641b59c4cec7fc60a218eab1419dad913ea008c30058722d5
         | 
    
        data/README.md
    CHANGED
    
    | @@ -2,15 +2,16 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            [](https://github.com/olimorris/hledger-forecast/actions/workflows/ci.yml)
         | 
| 4 4 |  | 
| 5 | 
            -
            A wrapper which builds on [hledger's](https://github.com/simonmichael/hledger) [forecasting](https://hledger.org/dev/hledger.html#forecasting) capability. Uses a ` | 
| 5 | 
            +
            A wrapper which builds on [hledger's](https://github.com/simonmichael/hledger) [forecasting](https://hledger.org/dev/hledger.html#forecasting) capability. Uses a `yaml` config file to generate forecasts whilst adding functionality for future cost rises (e.g. inflation) and the automatic tracking of planned transactions.
         | 
| 6 6 |  | 
| 7 7 | 
             
            See the [rationale](#brain-rationale) section for why this gem may be useful to you.
         | 
| 8 8 |  | 
| 9 9 | 
             
            ## :sparkles: Features
         | 
| 10 10 |  | 
| 11 | 
            -
            - :book: Uses simple  | 
| 11 | 
            +
            - :book: Uses a simple yaml file to generate forecasts which can be used with hledger
         | 
| 12 12 | 
             
            - :date: Can smartly track forecasted transactions against actuals
         | 
| 13 13 | 
             
            - :moneybag: Can automatically apply modifiers such as inflation/deflation to forecasts
         | 
| 14 | 
            +
            - :abacus: Supports calculated amounts in forecasts (uses the [Dentaku](https://github.com/rubysolo/dentaku) gem)
         | 
| 14 15 | 
             
            - :heavy_dollar_sign: Full currency support (uses the [RubyMoney](https://github.com/RubyMoney/money) gem)
         | 
| 15 16 | 
             
            - :computer: Simple and easy to use CLI
         | 
| 16 17 | 
             
            - :chart_with_upwards_trend: Summarize your forecasts by period and category and output to the CLI
         | 
| @@ -41,7 +42,7 @@ The available options are: | |
| 41 42 |  | 
| 42 43 | 
             
            ### Generate command
         | 
| 43 44 |  | 
| 44 | 
            -
            The `hledger-forecast generate` command will  | 
| 45 | 
            +
            The `hledger-forecast generate` command will generate a forecast  _from_ a `yaml` file _to_ a journal file. You can see the output of this command in the [example.journal](https://github.com/olimorris/hledger-forecast/blob/main/example.journal) file.
         | 
| 45 46 |  | 
| 46 47 | 
             
            The available options are:
         | 
| 47 48 |  | 
| @@ -174,6 +175,24 @@ monthly: | |
| 174 175 | 
             
                    to: "2025-01-01"
         | 
| 175 176 | 
             
            ```
         | 
| 176 177 |  | 
| 178 | 
            +
            ### Calculated amounts
         | 
| 179 | 
            +
             | 
| 180 | 
            +
            > **Note**: Calculations will be determined up to two decimal places
         | 
| 181 | 
            +
             | 
| 182 | 
            +
            It may be helpful to let the app calculate the forecasted amount in your transactions on your behalf. This can be especially useful if you're spreading a payment out over a number of months:
         | 
| 183 | 
            +
             | 
| 184 | 
            +
            ```yaml
         | 
| 185 | 
            +
            monthly:
         | 
| 186 | 
            +
              - account: "Liabilities:Amex"
         | 
| 187 | 
            +
                from: "2023-05-01"
         | 
| 188 | 
            +
                transactions:
         | 
| 189 | 
            +
                  - amount: "=5000/24"
         | 
| 190 | 
            +
                    category: "Expenses:House"
         | 
| 191 | 
            +
                    description: New Kitchen
         | 
| 192 | 
            +
            ```
         | 
| 193 | 
            +
             | 
| 194 | 
            +
            Simply ensure that the amount starts with an `=` sign, is enclosed in quotation marks and uses standard mathematical notations.
         | 
| 195 | 
            +
             | 
| 177 196 | 
             
            ### Tracking transactions
         | 
| 178 197 |  | 
| 179 198 | 
             
            > **Note**: Marking a transaction for tracking will ensure that it is only written into the forecast if it isn't found within a specified transaction file
         | 
| @@ -265,7 +284,7 @@ settings: | |
| 265 284 |  | 
| 266 285 | 
             
            Firstly, I've come to realise from reading countless blog and Reddit posts on [plain text accounting](https://plaintextaccounting.org), that everyone does it __completely__ differently! There is _great_ support in hledger for [forecasting](https://hledger.org/1.29/hledger.html#forecasting) using periodic transactions. Infact, it's nearly perfect for my needs. My only wishes were to be able to sum up monthly transactions much faster (so I can see my forecasted monthly I&E), apply future cost pressures more easily (such as inflation) and to be able to track and monitor specific transactions.
         | 
| 267 286 |  | 
| 268 | 
            -
            Regarding the latter; I may be expecting a material amount of money to leave my account in May (perhaps for a holiday booking). But maybe, that booking ends up leaving in July instead. Whilst I would have accounted for that expense in my forecast, it will  | 
| 287 | 
            +
            Regarding the latter; I may be expecting a material amount of money to leave my account in May (perhaps for a holiday booking). But maybe, that booking ends up leaving in July instead. Whilst I would have accounted for that expense in my forecast, it will be tied to some date in May. So if that transaction doesn't appear in the "actuals" of my May bank statement (which I import into hledger), it won't be included in my forecast at all (as the latest transaction period will be greater than the forecast period). The impact is that my forecasted balance in any future month could be $X better off than reality. Being able to automatically look out for these transactions, and include them if they're not present, is a nice time saver.
         | 
| 269 288 |  | 
| 270 289 | 
             
            Also, I like to look ahead up to 3 years at a time and understand what my bank balances might look like. For this to be really accurate, factors such as inflation and salary expectations should be included. This is where the idea for modifiers came in. Being able to apply a percentage to a given category between two dates and automatically have the impact included any extended forecasts.
         | 
| 271 290 |  | 
    
        data/example.journal
    CHANGED
    
    | @@ -1,8 +1,9 @@ | |
| 1 | 
            -
            ~ monthly from 2023-03-01  * Bonus, Salary, Food, New cell phone
         | 
| 1 | 
            +
            ~ monthly from 2023-03-01  * Bonus, Salary, Food, New cell phone, Holiday savings
         | 
| 2 2 | 
             
                Income:Bonus         £-100.00  ;  Bonus
         | 
| 3 3 | 
             
                Income:Salary        £-2,000.00;  Salary
         | 
| 4 4 | 
             
                Expenses:Food        £500.00   ;  Food
         | 
| 5 5 | 
             
                Expenses:Phone       £75.00    ;  New cell phone
         | 
| 6 | 
            +
                Expenses:Holiday     £208.33   ;  Holiday savings
         | 
| 6 7 | 
             
                Assets:Bank
         | 
| 7 8 |  | 
| 8 9 | 
             
            ~ monthly from 2023-03-01 to 2024-01-01  * Mortgage
         | 
| @@ -29,6 +30,10 @@ | |
| 29 30 | 
             
                Expenses:Car:Fuel    £150.00   ;  Car fuel
         | 
| 30 31 | 
             
                Assets:Bank
         | 
| 31 32 |  | 
| 33 | 
            +
            ~ 2023-06-01  * [TRACKED] Forecast new car cost
         | 
| 34 | 
            +
                Expenses:Car         £5,000.00 ;  Forecast new car cost
         | 
| 35 | 
            +
                Assets:Bank
         | 
| 36 | 
            +
             | 
| 32 37 | 
             
            = Expenses:Food date:2024-01-01..2024-12-31
         | 
| 33 38 | 
             
                Expenses:Food        *0.1      ;  Food - Inflation
         | 
| 34 39 | 
             
                Assets:Bank          *-0.1
         | 
    
        data/example.yml
    CHANGED
    
    
    
        data/hledger-forecast.gemspec
    CHANGED
    
    | @@ -14,6 +14,7 @@ Gem::Specification.new do |s| | |
| 14 14 | 
             
              s.license     = 'MIT'
         | 
| 15 15 |  | 
| 16 16 | 
             
              s.add_dependency "colorize", "~> 0.8.1"
         | 
| 17 | 
            +
              s.add_dependency "dentaku", "~> 3.5.1"
         | 
| 17 18 | 
             
              s.add_dependency "highline", "~> 2.1.0"
         | 
| 18 19 | 
             
              s.add_dependency "money", "~> 6.16.0"
         | 
| 19 20 | 
             
              s.add_dependency "terminal-table", "~> 3.0.2"
         | 
| @@ -2,9 +2,10 @@ module HledgerForecast | |
| 2 2 | 
             
              # Generates periodic transactions from a YAML file
         | 
| 3 3 | 
             
              class Generator
         | 
| 4 4 | 
             
                class << self
         | 
| 5 | 
            -
                  attr_accessor :options, :modified, :tracked
         | 
| 5 | 
            +
                  attr_accessor :calculator, :options, :modified, :tracked
         | 
| 6 6 | 
             
                end
         | 
| 7 7 |  | 
| 8 | 
            +
                self.calculator = {}
         | 
| 8 9 | 
             
                self.options = {}
         | 
| 9 10 | 
             
                self.modified = {}
         | 
| 10 11 | 
             
                self.tracked = {}
         | 
| @@ -24,6 +25,8 @@ module HledgerForecast | |
| 24 25 |  | 
| 25 26 | 
             
                  set_options(forecast_data)
         | 
| 26 27 |  | 
| 28 | 
            +
                  @calculator = Dentaku::Calculator.new
         | 
| 29 | 
            +
             | 
| 27 30 | 
             
                  output = ""
         | 
| 28 31 |  | 
| 29 32 | 
             
                  # Generate regular transactions
         | 
| @@ -75,7 +78,7 @@ module HledgerForecast | |
| 75 78 |  | 
| 76 79 | 
             
                    modified_transaction(from, to, account, transaction)
         | 
| 77 80 |  | 
| 78 | 
            -
                    output += output_transaction(transaction['category'], format_amount(transaction['amount']),
         | 
| 81 | 
            +
                    output += output_transaction(transaction['category'], format_amount(calculate_amount(transaction['amount'])),
         | 
| 79 82 | 
             
                                                 transaction['description'])
         | 
| 80 83 | 
             
                  end
         | 
| 81 84 |  | 
| @@ -107,7 +110,7 @@ module HledgerForecast | |
| 107 110 | 
             
                    modified_transaction(from, to, account, transaction)
         | 
| 108 111 |  | 
| 109 112 | 
             
                    output += "#{frequency} #{from} to #{to}  * #{transaction['description']}\n"
         | 
| 110 | 
            -
                    output += output_transaction(transaction['category'], format_amount(transaction['amount']),
         | 
| 113 | 
            +
                    output += output_transaction(transaction['category'], format_amount(calculate_amount(transaction['amount'])),
         | 
| 111 114 | 
             
                                                 transaction['description'])
         | 
| 112 115 | 
             
                    output += "    #{account}\n\n"
         | 
| 113 116 | 
             
                  end
         | 
| @@ -137,7 +140,7 @@ module HledgerForecast | |
| 137 140 |  | 
| 138 141 | 
             
                      modified_transaction(from, to, account, transaction)
         | 
| 139 142 |  | 
| 140 | 
            -
                      output += output_transaction(transaction['category'], format_amount(transaction['amount']),
         | 
| 143 | 
            +
                      output += output_transaction(transaction['category'], format_amount(calculate_amount(transaction['amount'])),
         | 
| 141 144 | 
             
                                                   transaction['description'])
         | 
| 142 145 | 
             
                    end
         | 
| 143 146 |  | 
| @@ -216,7 +219,7 @@ module HledgerForecast | |
| 216 219 | 
             
                end
         | 
| 217 220 |  | 
| 218 221 | 
             
                def self.track_transaction(from, to, account, transaction)
         | 
| 219 | 
            -
                  amount = transaction['amount']
         | 
| 222 | 
            +
                  amount = calculate_amount(transaction['amount'])
         | 
| 220 223 | 
             
                  transaction['amount'] = format_amount(amount)
         | 
| 221 224 | 
             
                  transaction['inverse_amount'] = format_amount(amount * -1)
         | 
| 222 225 |  | 
| @@ -240,6 +243,12 @@ module HledgerForecast | |
| 240 243 | 
             
                  map[period]
         | 
| 241 244 | 
             
                end
         | 
| 242 245 |  | 
| 246 | 
            +
                def self.calculate_amount(amount)
         | 
| 247 | 
            +
                  return amount unless amount.is_a?(String)
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                  @calculator.evaluate(amount.slice(1..-1))
         | 
| 250 | 
            +
                end
         | 
| 251 | 
            +
             | 
| 243 252 | 
             
                def self.format_amount(amount)
         | 
| 244 253 | 
             
                  Money.from_cents(amount.to_f * 100, @options[:currency]).format(
         | 
| 245 254 | 
             
                    symbol: @options[:show_symbol],
         | 
    
        data/lib/hledger_forecast.rb
    CHANGED
    
    
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            require_relative '../lib/hledger_forecast'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            config = <<~YAML
         | 
| 4 | 
            +
              settings:
         | 
| 5 | 
            +
                currency: GBP
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              monthly:
         | 
| 8 | 
            +
                - account: "Liabilities:Amex"
         | 
| 9 | 
            +
                  from: "2023-05-01"
         | 
| 10 | 
            +
                  transactions:
         | 
| 11 | 
            +
                    - amount: "=5000/24"
         | 
| 12 | 
            +
                      category: "Expenses:House"
         | 
| 13 | 
            +
                      description: New Kitchen
         | 
| 14 | 
            +
                    - amount: "=25*4.3"
         | 
| 15 | 
            +
                      category: "Expenses:Food"
         | 
| 16 | 
            +
                      description: Monthly food shop
         | 
| 17 | 
            +
                    - amount: "=(102.50+3.25)/2"
         | 
| 18 | 
            +
                      category: "Expenses:Food"
         | 
| 19 | 
            +
                      description: Random food
         | 
| 20 | 
            +
            YAML
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            output = <<~JOURNAL
         | 
| 23 | 
            +
              ~ monthly from 2023-05-01  * New Kitchen, Monthly food shop, Random food
         | 
| 24 | 
            +
                  Expenses:House    £208.33          ;  New Kitchen
         | 
| 25 | 
            +
                  Expenses:Food     £107.50          ;  Monthly food shop
         | 
| 26 | 
            +
                  Expenses:Food     £52.88           ;  Random food
         | 
| 27 | 
            +
                  Liabilities:Amex
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            JOURNAL
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            RSpec.describe 'generate' do
         | 
| 32 | 
            +
              it 'generates a forecast with correct CALCULATED transactions' do
         | 
| 33 | 
            +
                expect(HledgerForecast::Generator.generate(config)).to eq(output)
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
    
        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: 0. | 
| 4 | 
            +
              version: 0.4.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: 2023-05- | 
| 11 | 
            +
            date: 2023-05-09 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: colorize
         | 
| @@ -24,6 +24,20 @@ dependencies: | |
| 24 24 | 
             
                - - "~>"
         | 
| 25 25 | 
             
                  - !ruby/object:Gem::Version
         | 
| 26 26 | 
             
                    version: 0.8.1
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: dentaku
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: 3.5.1
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: 3.5.1
         | 
| 27 41 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 42 | 
             
              name: highline
         | 
| 29 43 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -105,6 +119,7 @@ files: | |
| 105 119 | 
             
            - lib/hledger_forecast/tracker.rb
         | 
| 106 120 | 
             
            - lib/hledger_forecast/version.rb
         | 
| 107 121 | 
             
            - spec/command_spec.rb
         | 
| 122 | 
            +
            - spec/computed_amounts_spec.rb
         | 
| 108 123 | 
             
            - spec/custom_spec.rb
         | 
| 109 124 | 
             
            - spec/half-yearly_spec.rb
         | 
| 110 125 | 
             
            - spec/modifier_spec.rb
         | 
| @@ -144,6 +159,7 @@ specification_version: 4 | |
| 144 159 | 
             
            summary: An extended wrapper around hledger's forecasting functionality
         | 
| 145 160 | 
             
            test_files:
         | 
| 146 161 | 
             
            - spec/command_spec.rb
         | 
| 162 | 
            +
            - spec/computed_amounts_spec.rb
         | 
| 147 163 | 
             
            - spec/custom_spec.rb
         | 
| 148 164 | 
             
            - spec/half-yearly_spec.rb
         | 
| 149 165 | 
             
            - spec/modifier_spec.rb
         |