ledgerjournal 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/ledgerjournal.svg)](https://badge.fury.io/rb/ledgerjournal)
|
3
4
|
[![Build Status](https://travis-ci.org/ralfebert/ledgerjournal.svg?branch=master)](https://travis-ci.org/github/ralfebert/ledgerjournal)
|
5
|
+
[![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](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
|
- - ">="
|