double_entry 0.10.0 → 0.10.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -1
  3. data/.rubocop.yml +55 -0
  4. data/.travis.yml +23 -12
  5. data/README.md +5 -1
  6. data/Rakefile +8 -3
  7. data/double_entry.gemspec +4 -3
  8. data/lib/active_record/locking_extensions.rb +28 -40
  9. data/lib/active_record/locking_extensions/log_subscriber.rb +4 -4
  10. data/lib/double_entry.rb +0 -2
  11. data/lib/double_entry/account.rb +13 -16
  12. data/lib/double_entry/account_balance.rb +0 -4
  13. data/lib/double_entry/balance_calculator.rb +4 -5
  14. data/lib/double_entry/configurable.rb +0 -2
  15. data/lib/double_entry/configuration.rb +2 -3
  16. data/lib/double_entry/errors.rb +2 -2
  17. data/lib/double_entry/line.rb +13 -16
  18. data/lib/double_entry/locking.rb +13 -18
  19. data/lib/double_entry/reporting.rb +2 -3
  20. data/lib/double_entry/reporting/aggregate.rb +90 -88
  21. data/lib/double_entry/reporting/aggregate_array.rb +58 -58
  22. data/lib/double_entry/reporting/day_range.rb +37 -35
  23. data/lib/double_entry/reporting/hour_range.rb +40 -37
  24. data/lib/double_entry/reporting/line_aggregate.rb +27 -28
  25. data/lib/double_entry/reporting/month_range.rb +67 -67
  26. data/lib/double_entry/reporting/time_range.rb +40 -38
  27. data/lib/double_entry/reporting/time_range_array.rb +3 -5
  28. data/lib/double_entry/reporting/week_range.rb +77 -78
  29. data/lib/double_entry/reporting/year_range.rb +27 -27
  30. data/lib/double_entry/transfer.rb +14 -15
  31. data/lib/double_entry/validation/line_check.rb +92 -86
  32. data/lib/double_entry/version.rb +1 -1
  33. data/lib/generators/double_entry/install/install_generator.rb +1 -2
  34. data/lib/generators/double_entry/install/templates/migration.rb +0 -2
  35. data/script/jack_hammer +1 -1
  36. data/spec/active_record/locking_extensions_spec.rb +45 -38
  37. data/spec/double_entry/account_balance_spec.rb +4 -5
  38. data/spec/double_entry/account_spec.rb +43 -44
  39. data/spec/double_entry/balance_calculator_spec.rb +6 -8
  40. data/spec/double_entry/configuration_spec.rb +14 -16
  41. data/spec/double_entry/line_spec.rb +25 -26
  42. data/spec/double_entry/locking_spec.rb +34 -39
  43. data/spec/double_entry/reporting/aggregate_array_spec.rb +8 -10
  44. data/spec/double_entry/reporting/aggregate_spec.rb +84 -44
  45. data/spec/double_entry/reporting/line_aggregate_spec.rb +7 -6
  46. data/spec/double_entry/reporting/month_range_spec.rb +109 -103
  47. data/spec/double_entry/reporting/time_range_array_spec.rb +145 -135
  48. data/spec/double_entry/reporting/time_range_spec.rb +36 -35
  49. data/spec/double_entry/reporting/week_range_spec.rb +82 -76
  50. data/spec/double_entry/reporting_spec.rb +9 -13
  51. data/spec/double_entry/transfer_spec.rb +13 -15
  52. data/spec/double_entry/validation/line_check_spec.rb +73 -79
  53. data/spec/double_entry_spec.rb +65 -68
  54. data/spec/generators/double_entry/install/install_generator_spec.rb +7 -10
  55. data/spec/spec_helper.rb +68 -10
  56. data/spec/support/accounts.rb +2 -4
  57. data/spec/support/double_entry_spec_helper.rb +4 -4
  58. data/spec/support/gemfiles/Gemfile.rails-3.2.x +1 -0
  59. metadata +31 -2
@@ -1,57 +1,59 @@
1
1
  # encoding: utf-8
2
2
  module DoubleEntry
3
- module Reporting
4
- class TimeRange
5
- attr_reader :start, :finish
6
- attr_reader :year, :month, :week, :day, :hour, :range_type
3
+ module Reporting
4
+ class TimeRange
5
+ attr_reader :start, :finish
6
+ attr_reader :year, :month, :week, :day, :hour, :range_type
7
7
 
8
- def self.make(options = {})
9
- @options = options
10
- case
11
- when (options[:year] and options[:week] and options[:day] and options[:hour])
8
+ def self.make(options = {})
9
+ @options = options
10
+ case
11
+ when options[:year] && options[:week] && options[:day] && options[:hour]
12
12
  HourRange.new(options)
13
- when (options[:year] and options[:week] and options[:day])
13
+ when options[:year] && options[:week] && options[:day]
14
14
  DayRange.new(options)
15
- when (options[:year] and options[:week])
15
+ when options[:year] && options[:week]
16
16
  WeekRange.new(options)
17
- when (options[:year] and options[:month])
17
+ when options[:year] && options[:month]
18
18
  MonthRange.new(options)
19
19
  when options[:year]
20
20
  YearRange.new(options)
21
21
  else
22
- raise "Invalid range information #{options}"
22
+ fail "Invalid range information #{options}"
23
+ end
23
24
  end
24
- end
25
25
 
26
- def self.range_from_time_for_period(start_time, period_name)
27
- case period_name
28
- when 'month'
29
- YearRange.from_time(start_time)
30
- when 'week'
31
- YearRange.from_time(start_time)
32
- when 'day'
33
- MonthRange.from_time(start_time)
34
- when 'hour'
35
- DayRange.from_time(start_time)
36
- end
37
- end
26
+ def self.range_from_time_for_period(start_time, period_name)
27
+ case period_name
28
+ when 'month'
29
+ YearRange.from_time(start_time)
30
+ when 'week'
31
+ YearRange.from_time(start_time)
32
+ when 'day'
33
+ MonthRange.from_time(start_time)
34
+ when 'hour'
35
+ DayRange.from_time(start_time)
36
+ end
37
+ end
38
38
 
39
- def include?(time)
40
- (time >= @start) and (time <= @finish)
41
- end
39
+ def include?(time)
40
+ time >= @start &&
41
+ time <= @finish
42
+ end
42
43
 
43
- def initialize(options)
44
- @year = options[:year]
45
- @range_type = options[:range_type] || :normal
46
- end
44
+ def initialize(options)
45
+ @year = options[:year]
46
+ @range_type = options[:range_type] || :normal
47
+ @month = @week = @day = @hour = nil
48
+ end
47
49
 
48
- def key
49
- "#{@year}:#{@month}:#{@week}:#{@day}:#{@hour}"
50
- end
50
+ def key
51
+ "#{@year}:#{@month}:#{@week}:#{@day}:#{@hour}"
52
+ end
51
53
 
52
- def human_readable_name
53
- self.class.name.gsub('DoubleEntry::Reporting::', '').gsub('Range', '')
54
+ def human_readable_name
55
+ self.class.name.gsub('DoubleEntry::Reporting::', '').gsub('Range', '')
56
+ end
54
57
  end
55
58
  end
56
- end
57
59
  end
@@ -2,7 +2,6 @@
2
2
  module DoubleEntry
3
3
  module Reporting
4
4
  class TimeRangeArray
5
-
6
5
  attr_reader :type, :require_start
7
6
  alias_method :require_start?, :require_start
8
7
 
@@ -14,7 +13,7 @@ module DoubleEntry
14
13
  def make(start = nil, finish = nil)
15
14
  start = start_range(start)
16
15
  finish = finish_range(finish)
17
- [ start ].tap do |array|
16
+ [start].tap do |array|
18
17
  while start != finish
19
18
  start = start.next
20
19
  array << start
@@ -23,7 +22,7 @@ module DoubleEntry
23
22
  end
24
23
 
25
24
  def start_range(start = nil)
26
- raise "Must specify start of range" if start.blank? && require_start?
25
+ fail 'Must specify start of range' if start.blank? && require_start?
27
26
  start_time = start ? Time.parse(start) : Reporting.configuration.start_of_business
28
27
  type.from_time(start_time)
29
28
  end
@@ -42,10 +41,9 @@ module DoubleEntry
42
41
 
43
42
  def self.make(range_type, start = nil, finish = nil)
44
43
  factory = FACTORIES[range_type]
45
- raise ArgumentError.new("Invalid range type '#{range_type}'") unless factory
44
+ fail ArgumentError, "Invalid range type '#{range_type}'" unless factory
46
45
  factory.make(start, finish)
47
46
  end
48
-
49
47
  end
50
48
  end
51
49
  end
@@ -1,108 +1,107 @@
1
1
  # encoding: utf-8
2
2
  module DoubleEntry
3
- module Reporting
4
- # We use a particularly crazy week numbering system: week 1 of any given year
5
- # is the first week with any days that fall into that year.
6
- #
7
- # So, for example, week 1 of 2011 starts on 27 Dec 2010.
8
- class WeekRange < TimeRange
9
-
10
- class << self
3
+ module Reporting
4
+ # We use a particularly crazy week numbering system: week 1 of any given year
5
+ # is the first week with any days that fall into that year.
6
+ #
7
+ # So, for example, week 1 of 2011 starts on 27 Dec 2010.
8
+ class WeekRange < TimeRange
9
+ class << self
10
+ def from_time(time)
11
+ date = time.to_date
12
+ week = date.cweek
13
+ year = date.end_of_week.year
14
+
15
+ if date.beginning_of_week.year != year
16
+ week = 1
17
+ elsif date.beginning_of_year.cwday > Date::DAYNAMES.index('Thursday')
18
+ week += 1
19
+ end
11
20
 
12
- def from_time(time)
13
- date = time.to_date
14
- week = date.cweek
15
- year = date.end_of_week.year
16
-
17
- if date.beginning_of_week.year != year
18
- week = 1
19
- elsif date.beginning_of_year.cwday > Date::DAYNAMES.index('Thursday')
20
- week += 1
21
+ new(:year => year, :week => week)
21
22
  end
22
23
 
23
- new(:year => year, :week => week)
24
- end
25
-
26
- def current
27
- from_time(Time.now)
28
- end
24
+ def current
25
+ from_time(Time.now)
26
+ end
29
27
 
30
- # Obtain a sequence of WeekRanges from the given start to the current
31
- # week.
32
- #
33
- # @option options :from [Time] Time of the first in the returned sequence
34
- # of WeekRanges.
35
- # @return [Array<WeekRange>]
36
- def reportable_weeks(options = {})
37
- week = options[:from] ? from_time(options[:from]) : earliest_week
38
- last_in_sequence = self.current
39
- [week].tap do |weeks|
40
- while week != last_in_sequence
41
- week = week.next
42
- weeks << week
28
+ # Obtain a sequence of WeekRanges from the given start to the current
29
+ # week.
30
+ #
31
+ # @option options :from [Time] Time of the first in the returned sequence
32
+ # of WeekRanges.
33
+ # @return [Array<WeekRange>]
34
+ def reportable_weeks(options = {})
35
+ week = options[:from] ? from_time(options[:from]) : earliest_week
36
+ last_in_sequence = current
37
+ [week].tap do |weeks|
38
+ while week != last_in_sequence
39
+ week = week.next
40
+ weeks << week
41
+ end
43
42
  end
44
43
  end
45
- end
46
44
 
47
- private
45
+ private
48
46
 
49
- def start_of_year(year)
50
- Time.local(year, 1, 1).beginning_of_week
51
- end
47
+ def start_of_year(year)
48
+ Time.local(year, 1, 1).beginning_of_week
49
+ end
52
50
 
53
- def earliest_week
54
- from_time(Reporting.configuration.start_of_business)
51
+ def earliest_week
52
+ from_time(Reporting.configuration.start_of_business)
53
+ end
55
54
  end
56
- end
57
55
 
58
- attr_reader :year, :week
56
+ attr_reader :year, :week
59
57
 
60
- def initialize(options = {})
61
- super options
58
+ def initialize(options = {})
59
+ super options
62
60
 
63
- if options.present?
64
- @week = options[:week]
61
+ if options.present?
62
+ @week = options[:week]
65
63
 
66
- @start = week_and_year_to_time(@week, @year)
67
- @finish = @start.end_of_week
64
+ @start = week_and_year_to_time(@week, @year)
65
+ @finish = @start.end_of_week
68
66
 
69
- @start = earliest_week.start if options[:range_type] == :all_time
67
+ @start = earliest_week.start if options[:range_type] == :all_time
68
+ end
70
69
  end
71
- end
72
70
 
73
- def previous
74
- from_time(@start - 1.week)
75
- end
71
+ def previous
72
+ from_time(@start - 1.week)
73
+ end
76
74
 
77
- def next
78
- from_time(@start + 1.week)
79
- end
75
+ def next
76
+ from_time(@start + 1.week)
77
+ end
80
78
 
81
- def ==(other)
82
- (self.week == other.week) && (self.year == other.year)
83
- end
79
+ def ==(other)
80
+ week == other.week &&
81
+ year == other.year
82
+ end
84
83
 
85
- def all_time
86
- self.class.new(:year => year, :week => week, :range_type => :all_time)
87
- end
84
+ def all_time
85
+ self.class.new(:year => year, :week => week, :range_type => :all_time)
86
+ end
88
87
 
89
- def to_s
90
- "#{year}, Week #{week}"
91
- end
88
+ def to_s
89
+ "#{year}, Week #{week}"
90
+ end
92
91
 
93
- private
92
+ private
94
93
 
95
- def from_time(time)
96
- self.class.from_time(time)
97
- end
94
+ def from_time(time)
95
+ self.class.from_time(time)
96
+ end
98
97
 
99
- def earliest_week
100
- self.class.send(:earliest_week)
101
- end
98
+ def earliest_week
99
+ self.class.send(:earliest_week)
100
+ end
102
101
 
103
- def week_and_year_to_time(week, year)
104
- self.class.send(:start_of_year, year) + (week - 1).weeks
102
+ def week_and_year_to_time(week, year)
103
+ self.class.send(:start_of_year, year) + (week - 1).weeks
104
+ end
105
105
  end
106
106
  end
107
- end
108
107
  end
@@ -1,40 +1,40 @@
1
1
  # encoding: utf-8
2
2
  module DoubleEntry
3
- module Reporting
4
- class YearRange < TimeRange
5
- attr_reader :year
3
+ module Reporting
4
+ class YearRange < TimeRange
5
+ attr_reader :year
6
6
 
7
- def initialize(options)
8
- super options
7
+ def initialize(options)
8
+ super options
9
9
 
10
- year_start = Time.local(@year, 1, 1)
11
- @start = year_start
12
- @finish = year_start.end_of_year
13
- end
10
+ year_start = Time.local(@year, 1, 1)
11
+ @start = year_start
12
+ @finish = year_start.end_of_year
13
+ end
14
14
 
15
- def self.current
16
- new(:year => Time.now.year)
17
- end
15
+ def self.current
16
+ new(:year => Time.now.year)
17
+ end
18
18
 
19
- def self.from_time(time)
20
- new(:year => time.year)
21
- end
19
+ def self.from_time(time)
20
+ new(:year => time.year)
21
+ end
22
22
 
23
- def ==(other)
24
- self.year == other.year
25
- end
23
+ def ==(other)
24
+ year == other.year
25
+ end
26
26
 
27
- def previous
28
- YearRange.new(:year => year - 1)
29
- end
27
+ def previous
28
+ YearRange.new(:year => year - 1)
29
+ end
30
30
 
31
- def next
32
- YearRange.new(:year => year + 1)
33
- end
31
+ def next
32
+ YearRange.new(:year => year + 1)
33
+ end
34
34
 
35
- def to_s
36
- year.to_s
35
+ def to_s
36
+ year.to_s
37
+ end
37
38
  end
38
39
  end
39
- end
40
40
  end
@@ -1,7 +1,6 @@
1
1
  # encoding: utf-8
2
2
  module DoubleEntry
3
3
  class Transfer
4
-
5
4
  class << self
6
5
  attr_writer :transfers, :code_max_length
7
6
 
@@ -17,7 +16,7 @@ module DoubleEntry
17
16
 
18
17
  # @api private
19
18
  def transfer(amount, options = {})
20
- raise TransferIsNegative if amount < Money.zero
19
+ fail TransferIsNegative if amount < Money.zero
21
20
  from = options[:from]
22
21
  to = options[:to]
23
22
  code = options[:code]
@@ -37,14 +36,14 @@ module DoubleEntry
37
36
  end
38
37
 
39
38
  def find!(from, to, code)
40
- transfer = find(from, to, code)
41
- raise TransferNotAllowed.new([from.identifier, to.identifier, code].inspect) unless transfer
42
- return transfer
39
+ find(from, to, code).tap do |transfer|
40
+ fail TransferNotAllowed, [from.identifier, to.identifier, code].inspect unless transfer
41
+ end
43
42
  end
44
43
 
45
44
  def <<(transfer)
46
45
  if _find(transfer.from, transfer.to, transfer.code)
47
- raise DuplicateTransfer.new
46
+ fail DuplicateTransfer
48
47
  else
49
48
  super(transfer)
50
49
  end
@@ -54,7 +53,9 @@ module DoubleEntry
54
53
 
55
54
  def _find(from, to, code)
56
55
  detect do |transfer|
57
- transfer.from == from and transfer.to == to and transfer.code == code
56
+ transfer.from == from &&
57
+ transfer.to == to &&
58
+ transfer.code == code
58
59
  end
59
60
  end
60
61
  end
@@ -66,18 +67,17 @@ module DoubleEntry
66
67
  @from = attributes[:from]
67
68
  @to = attributes[:to]
68
69
  if code.length > Transfer.code_max_length
69
- raise TransferCodeTooLongError.new(
70
- "transfer code '#{code}' is too long. Please limit it to #{Transfer.code_max_length} characters."
71
- )
70
+ fail TransferCodeTooLongError,
71
+ "transfer code '#{code}' is too long. Please limit it to #{Transfer.code_max_length} characters."
72
72
  end
73
73
  end
74
74
 
75
75
  def process(amount, from, to, code, detail)
76
- if from.scope_identity == to.scope_identity and from.identifier == to.identifier
77
- raise TransferNotAllowed.new("from and to are identical")
76
+ if from.scope_identity == to.scope_identity && from.identifier == to.identifier
77
+ fail TransferNotAllowed, 'from and to are identical'
78
78
  end
79
79
  if to.currency != from.currency
80
- raise MismatchedCurrencies.new("Missmatched currency (#{to.currency} <> #{from.currency})")
80
+ fail MismatchedCurrencies, "Missmatched currency (#{to.currency} <> #{from.currency})"
81
81
  end
82
82
  Locking.lock_accounts(from, to) do
83
83
  credit, debit = Line.new, Line.new
@@ -86,7 +86,7 @@ module DoubleEntry
86
86
  debit_balance = Locking.balance_for_locked_account(to)
87
87
 
88
88
  credit_balance.update_attribute :balance, credit_balance.balance - amount
89
- debit_balance.update_attribute :balance, debit_balance.balance + amount
89
+ debit_balance.update_attribute :balance, debit_balance.balance + amount
90
90
 
91
91
  credit.amount, debit.amount = -amount, amount
92
92
  credit.account, debit.account = from, to
@@ -102,6 +102,5 @@ module DoubleEntry
102
102
  credit.update_attribute :partner_id, debit.id
103
103
  end
104
104
  end
105
-
106
105
  end
107
106
  end