ledgerjournal 0.5.0 → 0.5.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 +4 -4
- data/.rubocop.yml +20 -0
- data/.travis.yml +1 -0
- data/Gemfile +5 -3
- data/Gemfile.lock +1 -1
- data/README.md +19 -5
- data/Rakefile +8 -6
- data/bin/console +4 -3
- data/ledgerjournal.gemspec +19 -14
- data/lib/ledgerjournal.rb +7 -7
- data/lib/ledgerjournal/journal.rb +23 -20
- data/lib/ledgerjournal/options.rb +15 -16
- data/lib/ledgerjournal/posting.rb +17 -21
- data/lib/ledgerjournal/transaction.rb +24 -22
- data/lib/ledgerjournal/version.rb +3 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b5770e988a14b6a2a45ada7b393d3a7ef857a822e17a5c4da2caebb50393257
|
4
|
+
data.tar.gz: 5fa7b153ddbddc3a175566086b6ebb8277f75428ad34ea065e13a524ec53b2a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3ce3d96d0e7b9cf22378d8b802b24077be2bcc56f5bffe0005c1b134d1d0621def7f74874ae1f260d5467ce02d7aa06092df30753414b4e59af225f5327fa37
|
7
|
+
data.tar.gz: c70b31078daf4176609859c4751d45d0c153dcee4cea59c22774a317aecd74163024b8b56d5f7f419b32460a7a4f7167f221c8cfd9cf5471fdf07ba8e51dc7ef
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Layout/LineLength:
|
2
|
+
Max: 200
|
3
|
+
|
4
|
+
Metrics/MethodLength:
|
5
|
+
Max: 30
|
6
|
+
|
7
|
+
Style/RedundantReturn:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Metrics/AbcSize:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Style/GuardClause:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Style/FormatStringToken:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Style/FormatString:
|
20
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
2
4
|
|
3
5
|
# Specify your gem's dependencies in ledgerjournal.gemspec
|
4
6
|
gemspec
|
5
7
|
|
6
|
-
gem
|
7
|
-
gem
|
8
|
+
gem 'minitest', '~> 5.0'
|
9
|
+
gem 'rake', '~> 12.0'
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# ledgerjournal
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/ledgerjournal)
|
3
4
|
[](https://travis-ci.org/github/ralfebert/ledgerjournal)
|
5
|
+
[](https://www.rubydoc.info/gems/ledgerjournal/)
|
4
6
|
|
5
7
|
ledgerjournal is a Ruby gem to read and write [ledger](https://www.ledger-cli.org/) accounting files.
|
6
|
-
For parsing, it uses the xml
|
8
|
+
For parsing, it uses the [ledger xml command](https://www.ledger-cli.org/3.0/doc/ledger3.html#The-xml-command). For outputting, it formats the ledger data as String in Ruby.
|
7
9
|
The ledger binary needs to be installed to parse and pretty-print.
|
8
10
|
|
9
11
|
## Usage
|
@@ -11,7 +13,7 @@ The ledger binary needs to be installed to parse and pretty-print.
|
|
11
13
|
Parsing a leger file:
|
12
14
|
|
13
15
|
```ruby
|
14
|
-
journal = Ledger::Journal.new(path: "
|
16
|
+
journal = Ledger::Journal.new(path: "example_journal.txt")
|
15
17
|
journal.transactions.each do |tx|
|
16
18
|
puts tx.date, tx.payee
|
17
19
|
end
|
@@ -20,11 +22,10 @@ end
|
|
20
22
|
Creating a ledger:
|
21
23
|
|
22
24
|
```ruby
|
23
|
-
journal = Ledger::Journal.new(
|
25
|
+
journal = Ledger::Journal.new()
|
24
26
|
|
25
27
|
journal.transactions << Ledger::Transaction.new(
|
26
28
|
date: Date.new(2020, 1, 2),
|
27
|
-
state: :cleared,
|
28
29
|
payee: 'Example Payee',
|
29
30
|
metadata: { "Foo" => "Bar", "Description" => "Example Transaction" },
|
30
31
|
postings: [
|
@@ -36,6 +37,19 @@ journal.transactions << Ledger::Transaction.new(
|
|
36
37
|
puts(journal.to_s)
|
37
38
|
```
|
38
39
|
|
40
|
+
### Locale-specific settings
|
41
|
+
|
42
|
+
By default ledgerjournal expectes the ledger default date format '%Y/%m/%d' and amounts with decimal point (1234.56). This is configurable:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
Ledger.defaults = Options.new(date_format: '%d.%m.%Y', decimal_comma: true)
|
46
|
+
```
|
47
|
+
|
48
|
+
or:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
Ledger.defaults = Ledger::Options.for_locale(:de)
|
52
|
+
```
|
39
53
|
|
40
54
|
## Installation
|
41
55
|
|
@@ -59,4 +73,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/ralfeb
|
|
59
73
|
|
60
74
|
## License
|
61
75
|
|
62
|
-
|
76
|
+
ledgerjournal is released under the MIT License. See LICENSE.md.
|
data/Rakefile
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rake/testtask'
|
3
5
|
|
4
6
|
Rake::TestTask.new(:test) do |t|
|
5
|
-
t.libs <<
|
6
|
-
t.libs <<
|
7
|
-
t.test_files = FileList[
|
7
|
+
t.libs << 'test'
|
8
|
+
t.libs << 'lib'
|
9
|
+
t.test_files = FileList['test/**/*_test.rb']
|
8
10
|
end
|
9
11
|
|
10
|
-
task :
|
12
|
+
task default: :test
|
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'ledgerjournal'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "ledgerjournal"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start(__FILE__)
|
data/ledgerjournal.gemspec
CHANGED
@@ -1,30 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'lib/ledgerjournal/version'
|
2
4
|
|
3
5
|
Gem::Specification.new do |spec|
|
4
|
-
spec.name =
|
6
|
+
spec.name = 'ledgerjournal'
|
5
7
|
spec.version = Ledger::VERSION
|
6
8
|
spec.licenses = ['MIT']
|
7
|
-
spec.authors = [
|
8
|
-
spec.email = [
|
9
|
+
spec.authors = ['Ralf Ebert']
|
10
|
+
spec.email = ['ralf.ebert@gmail.com']
|
9
11
|
|
10
|
-
spec.summary =
|
11
|
-
spec.description =
|
12
|
+
spec.summary = 'Library to read and write ledger accounting files.'
|
13
|
+
spec.description = 'ledgerjournal is a Ruby gem to read and write ledger accounting files.
|
12
14
|
For parsing, it uses the xml output from ledger. For outputting, it formats the ledger data to String in custom Ruby code.
|
13
|
-
The ledger binary needs to be installed to parse and pretty-print.
|
14
|
-
spec.homepage =
|
15
|
-
spec.required_ruby_version = Gem::Requirement.new(
|
15
|
+
The ledger binary needs to be installed to parse and pretty-print.'
|
16
|
+
spec.homepage = 'https://github.com/ralfebert/ledgerjournal'
|
17
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
16
18
|
|
17
|
-
spec.metadata
|
18
|
-
|
19
|
+
spec.metadata = {
|
20
|
+
'homepage_uri' => spec.homepage,
|
21
|
+
'source_code_uri' => 'https://github.com/ralfebert/ledgerjournal.git',
|
22
|
+
'bug_tracker_uri' => 'https://github.com/ralfebert/ledgerjournal/issues',
|
23
|
+
'documentation_uri' => "https://www.rubydoc.info/gems/ledgerjournal/#{Ledger::VERSION}"
|
24
|
+
}
|
19
25
|
|
20
26
|
# Specify which files should be added to the gem when it is released.
|
21
27
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
-
spec.files
|
28
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
23
29
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
24
30
|
end
|
25
|
-
spec.require_paths = [
|
26
|
-
|
31
|
+
spec.require_paths = ['lib']
|
32
|
+
|
27
33
|
spec.add_dependency 'nokogiri', '~> 1.10'
|
28
34
|
spec.add_dependency 'open4', '~> 1.3'
|
29
|
-
|
30
35
|
end
|
data/lib/ledgerjournal.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ledgerjournal/version'
|
4
|
+
require 'ledgerjournal/options'
|
5
|
+
require 'ledgerjournal/journal'
|
6
|
+
require 'ledgerjournal/transaction'
|
7
|
+
require 'ledgerjournal/posting'
|
6
8
|
|
7
9
|
# Ledger provides classes to read and write ledger accounting files.
|
8
10
|
module Ledger
|
9
|
-
|
10
11
|
# Error class for errors handling ledger files
|
11
12
|
class Error < StandardError; end
|
12
|
-
|
13
13
|
end
|
@@ -1,13 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'shellwords'
|
2
4
|
require 'nokogiri'
|
3
5
|
require 'open4'
|
6
|
+
require 'set'
|
4
7
|
|
5
8
|
module Ledger
|
6
|
-
|
7
9
|
# Represents a ledger journal
|
8
10
|
# @see https://www.ledger-cli.org/3.0/doc/ledger3.html#Journal-Format
|
9
11
|
class Journal
|
10
|
-
|
11
12
|
# @return [Array<Ledger::Transaction>] list of transactions in journal
|
12
13
|
attr_accessor :transactions
|
13
14
|
# @return [String] path path to ledger file
|
@@ -20,8 +21,9 @@ module Ledger
|
|
20
21
|
@transactions = []
|
21
22
|
if path
|
22
23
|
@path = path
|
23
|
-
raise Error
|
24
|
-
|
24
|
+
raise Error, "#{@path} not found" unless File.exist?(@path)
|
25
|
+
|
26
|
+
args = ["-f #{@path.shellescape}", ledger_args].compact.join(' ')
|
25
27
|
read_ledger(ledger_args: args)
|
26
28
|
end
|
27
29
|
end
|
@@ -29,20 +31,20 @@ module Ledger
|
|
29
31
|
# @param [Ledger::Journal] other
|
30
32
|
# @return [Boolean] true if the other journal contains equal transactions
|
31
33
|
def ==(other)
|
32
|
-
|
34
|
+
transactions == other.transactions
|
33
35
|
end
|
34
36
|
|
35
37
|
# @return [String] returns the transactions in the journal formatted as string
|
36
38
|
# @param [Boolean] pretty_print calls ledger to format the journal if true
|
37
39
|
def to_s(pretty_print: true)
|
38
|
-
str =
|
40
|
+
str = transactions.map(&:to_s).join("\n\n")
|
39
41
|
if pretty_print
|
40
42
|
begin
|
41
|
-
str = Ledger.defaults.run(
|
42
|
-
str = str.lines.map
|
43
|
-
rescue =>
|
43
|
+
str = Ledger.defaults.run('-f - print', stdin: str)
|
44
|
+
str = str.lines.map(&:rstrip).join("\n") + "\n"
|
45
|
+
rescue StandardError => e
|
44
46
|
# return an unformatted string if an error occurs
|
45
|
-
puts "Couldn't format transaction log: #{
|
47
|
+
puts "Couldn't format transaction log: #{e}"
|
46
48
|
end
|
47
49
|
end
|
48
50
|
return str
|
@@ -50,23 +52,24 @@ module Ledger
|
|
50
52
|
|
51
53
|
# If the journal was opened from a file, save/overwrite the file with the transactions from this journal.
|
52
54
|
def save!
|
53
|
-
raise Error
|
54
|
-
|
55
|
+
raise Error, 'Journal was not read from path, cannot save' unless @path
|
56
|
+
|
57
|
+
File.write(@path, to_s)
|
55
58
|
end
|
56
59
|
|
57
|
-
# returns all transactions that have a posting for the given
|
58
|
-
# @param [String] account account name
|
60
|
+
# returns all transactions that have a posting for one of the given accounts
|
61
|
+
# @param [String, Array<String>, Set<String>] account account name(s)
|
59
62
|
def transactions_with_account(account)
|
60
|
-
|
63
|
+
accounts = account.is_a?(Enumerable) ? Set.new(account) : Set[account]
|
64
|
+
@transactions.select { |tx| accounts.intersect?(Set.new(tx.postings.map(&:account))) }
|
61
65
|
end
|
62
66
|
|
63
67
|
private
|
64
|
-
|
65
|
-
|
68
|
+
|
69
|
+
def read_ledger(ledger_args: '')
|
70
|
+
xml_result = Ledger.defaults.run(ledger_args + ' xml')
|
66
71
|
xml = Nokogiri::XML(xml_result)
|
67
|
-
@transactions = xml.css(
|
72
|
+
@transactions = xml.css('transaction').map { |tx_xml| Transaction.parse_xml(tx_xml) }
|
68
73
|
end
|
69
|
-
|
70
74
|
end
|
71
|
-
|
72
75
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module Ledger
|
3
4
|
# Options for interaction with ledger-cli
|
4
5
|
class Options
|
5
|
-
|
6
6
|
# @param [String] date_format like '%Y/%m/%d' to pass to ledger-cli
|
7
|
-
# @param [Boolean] decimal_comma pass true to use
|
7
|
+
# @param [Boolean] decimal_comma pass true to use a comma as decimal separator, otherwise a dot is used
|
8
8
|
def initialize(date_format:, decimal_comma:)
|
9
9
|
@date_format = date_format
|
10
10
|
@decimal_comma = decimal_comma
|
@@ -20,14 +20,14 @@ module Ledger
|
|
20
20
|
when :de
|
21
21
|
Options.new(date_format: '%d.%m.%Y', decimal_comma: true)
|
22
22
|
else
|
23
|
-
raise Error
|
23
|
+
raise Error, "Unknown locale for ledger options: #{locale}, supported are :en, :de"
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
27
|
# @param [String] string decimal amount as String (like '12.34')
|
28
28
|
# @return [BigDecimal] the given string as BigDecimal, parsed according the decimal_comma setting
|
29
29
|
def parse_amount(string)
|
30
|
-
BigDecimal(
|
30
|
+
BigDecimal(@decimal_comma ? string.gsub('.', '').gsub(',', '.') : string)
|
31
31
|
end
|
32
32
|
|
33
33
|
# Formats the given value as String
|
@@ -41,7 +41,7 @@ module Ledger
|
|
41
41
|
str.gsub!('.', ',') if @decimal_comma
|
42
42
|
return str
|
43
43
|
else
|
44
|
-
raise Error
|
44
|
+
raise Error, "Unknown value type #{value.class}"
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -50,23 +50,23 @@ module Ledger
|
|
50
50
|
# @param [String] stdin stdin text to pass
|
51
51
|
# @return [String] stdout result of calling ledger
|
52
52
|
def run(args, stdin: nil)
|
53
|
-
output =
|
54
|
-
error =
|
53
|
+
output = String.new
|
54
|
+
error = String.new
|
55
55
|
begin
|
56
|
-
Open4.spawn("ledger #{cmdline_options} #{args}", :
|
57
|
-
rescue => e
|
58
|
-
raise Error
|
56
|
+
Open4.spawn("ledger #{cmdline_options} #{args}", stdin: stdin, stdout: output, stderr: error)
|
57
|
+
rescue StandardError => e
|
58
|
+
raise Error, "#{e}: #{error}"
|
59
59
|
end
|
60
60
|
return output
|
61
61
|
end
|
62
62
|
|
63
63
|
private
|
64
|
+
|
64
65
|
def cmdline_options
|
65
|
-
args = [
|
66
|
-
args <<
|
67
|
-
return args.join(
|
66
|
+
args = ['--args-only', "--date-format #{@date_format}", "--input-date-format #{@date_format}"]
|
67
|
+
args << '--decimal-comma' if @decimal_comma
|
68
|
+
return args.join(' ')
|
68
69
|
end
|
69
|
-
|
70
70
|
end
|
71
71
|
|
72
72
|
@defaults = Options.for_locale(:en)
|
@@ -75,5 +75,4 @@ module Ledger
|
|
75
75
|
# @attr [Ledger::Options] defaults options to use when interacting with ledger-cli, by default settings for locale :en are used
|
76
76
|
attr_accessor :defaults
|
77
77
|
end
|
78
|
-
|
79
78
|
end
|
@@ -1,17 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
require 'bigdecimal'
|
3
5
|
|
4
6
|
module Ledger
|
5
|
-
|
6
7
|
# @attr [String] account
|
7
8
|
# @attr [String] currency
|
8
9
|
# @attr [BigDecimal] amount
|
9
10
|
# @attr [BigDecimal] balance_assignment if a balance_assignment is set, ledger-cli checks the balance of the account after this posting
|
10
11
|
# @attr [Hash<String, String>] metadata
|
11
12
|
class Posting
|
12
|
-
|
13
13
|
attr_accessor :account, :currency, :amount, :balance_assignment, :metadata
|
14
|
-
|
14
|
+
|
15
15
|
def initialize(account:, currency:, amount:, balance_assignment: nil, metadata: {})
|
16
16
|
@account = account
|
17
17
|
@currency = currency
|
@@ -19,13 +19,14 @@ module Ledger
|
|
19
19
|
@balance_assignment = balance_assignment
|
20
20
|
@metadata = metadata
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def self.parse_xml(xml)
|
24
24
|
balance_assignment = nil
|
25
25
|
currency = xml.xpath('post-amount/amount/commodity/symbol').text
|
26
|
-
if xml_balance = xml.xpath('balance-assignment').first
|
26
|
+
if (xml_balance = xml.xpath('balance-assignment').first)
|
27
27
|
balance_currency = xml_balance.xpath('commodity/symbol').text
|
28
|
-
raise Error
|
28
|
+
raise Error, "Posting currency #{currency} doesn't match assignment currency #{balance_currency}" if currency != balance_currency
|
29
|
+
|
29
30
|
balance_assignment = Ledger.defaults.parse_amount(xml_balance.xpath('quantity').text)
|
30
31
|
end
|
31
32
|
Posting.new(
|
@@ -33,31 +34,26 @@ module Ledger
|
|
33
34
|
currency: currency,
|
34
35
|
amount: Ledger.defaults.parse_amount(xml.xpath('post-amount/amount/quantity').text),
|
35
36
|
balance_assignment: balance_assignment,
|
36
|
-
metadata: Hash[xml.xpath(
|
37
|
+
metadata: Hash[xml.xpath('metadata/value').collect { |m| [m['key'], m.xpath('string').text] }]
|
37
38
|
)
|
38
39
|
end
|
39
|
-
|
40
|
+
|
40
41
|
def ==(other)
|
41
|
-
self.class == other.class &&
|
42
|
+
self.class == other.class && all_fields == other.all_fields
|
42
43
|
end
|
43
|
-
|
44
|
+
|
44
45
|
def to_s
|
45
|
-
posting_line = "#{
|
46
|
-
|
47
|
-
posting_line += " = #{self.currency} #{Ledger.defaults.format(self.balance_assignment)}"
|
48
|
-
end
|
46
|
+
posting_line = "#{account} #{currency} #{Ledger.defaults.format(amount)}"
|
47
|
+
posting_line += " = #{currency} #{Ledger.defaults.format(balance_assignment)}" if balance_assignment
|
49
48
|
lines = [posting_line]
|
50
|
-
unless
|
51
|
-
lines += metadata.to_a.sort {|a,b| a[0].casecmp(b[0]) }.collect{|m| "; #{m[0]}: #{m[1]}" }
|
52
|
-
end
|
49
|
+
lines += metadata.to_a.sort { |a, b| a[0].casecmp(b[0]) }.collect { |m| "; #{m[0]}: #{m[1]}" } unless metadata.empty?
|
53
50
|
return lines.join("\n")
|
54
51
|
end
|
55
|
-
|
52
|
+
|
56
53
|
protected
|
54
|
+
|
57
55
|
def all_fields
|
58
|
-
|
56
|
+
instance_variables.map { |variable| instance_variable_get variable }
|
59
57
|
end
|
60
|
-
|
61
58
|
end
|
62
|
-
|
63
59
|
end
|
@@ -1,14 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
|
3
5
|
module Ledger
|
4
|
-
|
5
6
|
# @attr [Date] date
|
6
7
|
# @attr [Symbol] state state of transaction, can be :cleared or :pending
|
7
8
|
# @attr [String] payee
|
8
9
|
# @attr [Hash<String, String>] metadata
|
9
10
|
# @attr [Array<Ledger::Posting>] postings
|
10
11
|
class Transaction
|
11
|
-
|
12
12
|
attr_accessor :date, :state, :payee, :metadata, :postings
|
13
13
|
|
14
14
|
def initialize(date:, state: :cleared, payee:, metadata: {}, postings:)
|
@@ -21,44 +21,46 @@ module Ledger
|
|
21
21
|
|
22
22
|
def self.parse_xml(xml)
|
23
23
|
Transaction.new(
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
date: Date.strptime(xml.xpath('date').text, '%Y/%m/%d'),
|
25
|
+
payee: xml.xpath('payee').text,
|
26
|
+
state: xml['state'].to_sym,
|
27
|
+
metadata: Hash[xml.xpath('metadata/value').collect { |m| [m['key'], m.xpath('string').text] }],
|
28
|
+
postings: xml.xpath('postings/posting').map { |posting_xml| Posting.parse_xml(posting_xml) }
|
29
29
|
)
|
30
30
|
end
|
31
31
|
|
32
32
|
def ==(other)
|
33
|
-
self.class == other.class &&
|
33
|
+
self.class == other.class && all_fields == other.all_fields
|
34
34
|
end
|
35
35
|
|
36
36
|
def to_s
|
37
|
-
date_str = Ledger.defaults.format(
|
38
|
-
states = {:
|
39
|
-
lines = ["#{date_str} #{states[
|
37
|
+
date_str = Ledger.defaults.format(date)
|
38
|
+
states = { pending: '!', cleared: '*' }
|
39
|
+
lines = ["#{date_str} #{states[state]} #{payee}"]
|
40
40
|
|
41
|
-
unless
|
42
|
-
lines += metadata.to_a.sort { |a, b| a[0].casecmp(b[0]) }.collect { |m| " ; #{m[0]}: #{m[1]}" }
|
43
|
-
end
|
41
|
+
lines += metadata.to_a.sort { |a, b| a[0].casecmp(b[0]) }.collect { |m| " ; #{m[0]}: #{m[1]}" } unless metadata.empty?
|
44
42
|
|
45
|
-
lines +=
|
43
|
+
lines += postings.map { |posting| posting.to_s.lines.map { |line| ' ' + line }.join }
|
46
44
|
|
47
45
|
return lines.join("\n") + "\n"
|
48
46
|
end
|
49
47
|
|
50
|
-
# @param [String]
|
51
|
-
# @return [Ledger::Posting]
|
52
|
-
def posting_for_account(
|
53
|
-
|
48
|
+
# @param [String, Array<String>] accounts name(s)
|
49
|
+
# @return [Ledger::Posting] Returns the first posting that matches one of the given account names
|
50
|
+
def posting_for_account(accounts)
|
51
|
+
accounts = [accounts] unless accounts.is_a?(Enumerable)
|
52
|
+
accounts.each do |account|
|
53
|
+
postings.each do |posting|
|
54
|
+
return posting if account == posting.account
|
55
|
+
end
|
56
|
+
end
|
57
|
+
return nil
|
54
58
|
end
|
55
59
|
|
56
60
|
protected
|
57
61
|
|
58
62
|
def all_fields
|
59
|
-
|
63
|
+
instance_variables.map { |variable| instance_variable_get(variable) }
|
60
64
|
end
|
61
|
-
|
62
65
|
end
|
63
|
-
|
64
66
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ledgerjournal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ralf Ebert
|
@@ -49,6 +49,7 @@ extensions: []
|
|
49
49
|
extra_rdoc_files: []
|
50
50
|
files:
|
51
51
|
- ".gitignore"
|
52
|
+
- ".rubocop.yml"
|
52
53
|
- ".travis.yml"
|
53
54
|
- Gemfile
|
54
55
|
- Gemfile.lock
|
@@ -70,6 +71,8 @@ licenses:
|
|
70
71
|
metadata:
|
71
72
|
homepage_uri: https://github.com/ralfebert/ledgerjournal
|
72
73
|
source_code_uri: https://github.com/ralfebert/ledgerjournal.git
|
74
|
+
bug_tracker_uri: https://github.com/ralfebert/ledgerjournal/issues
|
75
|
+
documentation_uri: https://www.rubydoc.info/gems/ledgerjournal/0.5.1
|
73
76
|
post_install_message:
|
74
77
|
rdoc_options: []
|
75
78
|
require_paths:
|
@@ -78,7 +81,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
81
|
requirements:
|
79
82
|
- - ">="
|
80
83
|
- !ruby/object:Gem::Version
|
81
|
-
version: 2.
|
84
|
+
version: 2.5.0
|
82
85
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
86
|
requirements:
|
84
87
|
- - ">="
|