morty 0.0.1 → 0.1.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 +5 -5
- data/.github/dependabot.yml +12 -0
- data/.github/workflows/ci.yml +107 -0
- data/.gitignore +17 -3
- data/.rubocop.yml +20 -0
- data/Appraisals +24 -0
- data/Gemfile +28 -1
- data/LICENSE +21 -0
- data/README.md +37 -7
- data/Rakefile +37 -0
- data/app/models/morty/account.rb +37 -0
- data/app/models/morty/account_type.rb +7 -0
- data/app/models/morty/activity.rb +147 -0
- data/app/models/morty/activity_type.rb +7 -0
- data/app/models/morty/application_record.rb +6 -0
- data/app/models/morty/entry.rb +24 -0
- data/app/models/morty/entry_type.rb +23 -0
- data/app/models/morty/ledger.rb +8 -0
- data/config/routes.rb +2 -0
- data/config.ru +7 -0
- data/cucumber.yml +2 -0
- data/db/migrate/20260224063053_create_morty_schema.rb +17 -0
- data/db/seeds.rb +18 -0
- data/db/sql/create_morty_schema.sql +479 -0
- data/features/accountant.feature +47 -0
- data/features/adjustment.feature +79 -0
- data/features/cancel.feature +130 -0
- data/features/daily.feature +42 -0
- data/features/default.feature +33 -0
- data/features/ledger.feature +57 -0
- data/features/retroactive.feature +92 -0
- data/features/return.feature +112 -0
- data/features/reversal.feature +57 -0
- data/features/simulation.feature +128 -0
- data/features/support/accountants/adjusting_accountant.rb +34 -0
- data/features/support/accountants/daily_accountant.rb +13 -0
- data/features/support/accountants/default_accountant.rb +2 -0
- data/features/support/accountants/defaulting_accountant.rb +32 -0
- data/features/support/accountants/multiple_ledgers_accountant.rb +51 -0
- data/features/support/accountants/simulating_accountant.rb +36 -0
- data/features/support/accountants/sourceless_accountant.rb +2 -0
- data/features/support/accountants/waterfalling_accountant.rb +15 -0
- data/features/support/env.rb +17 -0
- data/features/waterfall.feature +34 -0
- data/gemfiles/rails_7.0.gemfile +30 -0
- data/gemfiles/rails_7.0.gemfile.lock +494 -0
- data/gemfiles/rails_7.1.gemfile +30 -0
- data/gemfiles/rails_7.1.gemfile.lock +543 -0
- data/gemfiles/rails_7.2.gemfile +30 -0
- data/gemfiles/rails_7.2.gemfile.lock +539 -0
- data/gemfiles/rails_8.0.gemfile +30 -0
- data/gemfiles/rails_8.0.gemfile.lock +536 -0
- data/gemfiles/rails_8.1.gemfile +30 -0
- data/gemfiles/rails_8.1.gemfile.lock +538 -0
- data/lib/morty/accountant.rb +332 -0
- data/lib/morty/adjustment.rb +64 -0
- data/lib/morty/book.rb +54 -0
- data/lib/morty/context/activity.rb +52 -0
- data/lib/morty/context/daily.rb +23 -0
- data/lib/morty/context/simulation.rb +26 -0
- data/lib/morty/cucumber/helpers.rb +27 -0
- data/lib/morty/cucumber/steps.rb +191 -0
- data/lib/morty/diff.rb +71 -0
- data/lib/morty/dsl.rb +86 -0
- data/lib/morty/engine.rb +21 -0
- data/lib/morty/error.rb +3 -0
- data/lib/morty/event.rb +27 -0
- data/lib/morty/list/activity.rb +57 -0
- data/lib/morty/rate.rb +59 -0
- data/lib/morty/schedule.rb +36 -0
- data/lib/morty/seed.rb +60 -0
- data/lib/morty/source.rb +19 -0
- data/lib/morty/tasks/morty_tasks.rake +4 -0
- data/lib/morty/version.rb +1 -1
- data/lib/morty.rb +27 -1
- data/morty.gemspec +22 -19
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/jobs/application_job.rb +7 -0
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +28 -0
- data/spec/dummy/app/views/pwa/manifest.json.erb +22 -0
- data/spec/dummy/app/views/pwa/service-worker.js +26 -0
- data/spec/dummy/bin/ci +6 -0
- data/spec/dummy/bin/dev +2 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +35 -0
- data/spec/dummy/config/application.rb +48 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/cable.yml +10 -0
- data/spec/dummy/config/ci.rb +15 -0
- data/spec/dummy/config/database.yml +15 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +47 -0
- data/spec/dummy/config/environments/test.rb +53 -0
- data/spec/dummy/config/initializers/content_security_policy.rb +29 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +8 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/locales/en.yml +31 -0
- data/spec/dummy/config/puma.rb +39 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/storage.yml +27 -0
- data/spec/dummy/config.ru +6 -0
- data/spec/dummy/db/seeds.rb +52 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/400.html +135 -0
- data/spec/dummy/public/404.html +135 -0
- data/spec/dummy/public/406-unsupported-browser.html +135 -0
- data/spec/dummy/public/422.html +135 -0
- data/spec/dummy/public/500.html +135 -0
- data/spec/dummy/public/icon.png +0 -0
- data/spec/dummy/public/icon.svg +3 -0
- data/spec/lib/accountant_spec.rb +236 -0
- data/spec/lib/book_spec.rb +91 -0
- data/spec/lib/diff_spec.rb +102 -0
- data/spec/lib/event_spec.rb +53 -0
- data/spec/lib/list/activity_spec.rb +117 -0
- data/spec/lib/schedule_spec.rb +106 -0
- data/spec/lib/source_spec.rb +31 -0
- data/spec/models/account_spec.rb +48 -0
- data/spec/models/activity_spec.rb +139 -0
- data/spec/models/entry_spec.rb +41 -0
- data/spec/models/entry_type_spec.rb +43 -0
- data/spec/rate_spec.rb +83 -0
- data/spec/spec_helper.rb +36 -0
- data/spec/support/test_helpers.rb +25 -0
- metadata +193 -16
- data/LICENSE.txt +0 -22
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class DefaultingAccountant < Morty::Accountant
|
|
2
|
+
balance :accruing, %w[principal principal_late]
|
|
3
|
+
|
|
4
|
+
activity :issue do
|
|
5
|
+
entry :principal, :cash, amount
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
activity :interest do
|
|
9
|
+
entry :interest, :revenue, amount
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
daily do
|
|
13
|
+
activity :interest, today, rate.daily * balances[:accruing]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
daily_guard do
|
|
17
|
+
accountant.activities.with_type(:interest).none? { |a| a.effective_date == accountant.date }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
waterfall :default, limit: :cr, entries: <<~END
|
|
21
|
+
interest_late interest
|
|
22
|
+
principal_late principal
|
|
23
|
+
END
|
|
24
|
+
|
|
25
|
+
waterfall :payment, limit: :cr, complete: true, entries: <<~END
|
|
26
|
+
cash interest_late
|
|
27
|
+
cash principal_late
|
|
28
|
+
cash interest
|
|
29
|
+
cash principal
|
|
30
|
+
cash payable
|
|
31
|
+
END
|
|
32
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
class MultipleLedgersAccountant < Morty::Accountant
|
|
2
|
+
balance :accruing, %w[principal]
|
|
3
|
+
balance :varying, %w[principal]
|
|
4
|
+
|
|
5
|
+
daily do
|
|
6
|
+
activity :interest, today
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
daily_guard do
|
|
10
|
+
true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
ledger :aggressive do
|
|
14
|
+
balance :accruing, %w[principal]
|
|
15
|
+
balance :varying, %w[principal cash]
|
|
16
|
+
|
|
17
|
+
activity :issue do
|
|
18
|
+
entry :principal, :cash, amount
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
activity :payment do
|
|
22
|
+
waterfall amount, limit: :cr, complete: true, entries: <<~END
|
|
23
|
+
cash interest
|
|
24
|
+
cash principal
|
|
25
|
+
cash payable
|
|
26
|
+
END
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
activity :interest do
|
|
30
|
+
entry :interest, :revenue, accountant.rate_for(activity.effective_date).daily * balances[:accruing] * 10
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
ledger :default do
|
|
35
|
+
activity :issue do
|
|
36
|
+
entry :principal, :cash, amount
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
activity :payment do
|
|
40
|
+
waterfall amount, limit: :cr, complete: true, entries: <<~END
|
|
41
|
+
cash interest
|
|
42
|
+
cash principal
|
|
43
|
+
cash payable
|
|
44
|
+
END
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
activity :interest do
|
|
48
|
+
entry :interest, :revenue, accountant.rate_for(activity.effective_date).daily * balances[:accruing]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
class SimulatingAccountant < Morty::Accountant
|
|
2
|
+
balance :accruing, %w[principal]
|
|
3
|
+
|
|
4
|
+
activity :issue do
|
|
5
|
+
entry :principal, :cash, amount
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
waterfall :payment, limit: :cr, complete: true, entries: <<~END
|
|
9
|
+
cash late_fee
|
|
10
|
+
cash interest
|
|
11
|
+
cash principal
|
|
12
|
+
cash payable
|
|
13
|
+
END
|
|
14
|
+
|
|
15
|
+
activity :interest do
|
|
16
|
+
if rate = accountant.rate_for(accountant.date.yesterday)
|
|
17
|
+
amount ||= (rate.daily_for(accountant.date.yesterday) * balances[:accruing]).floor(2)
|
|
18
|
+
|
|
19
|
+
entry :interest, :revenue, amount
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
activity :late_fee do
|
|
24
|
+
entry :late_fee, :late_fee_revenue, amount
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
daily do
|
|
28
|
+
activity :interest, today
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
daily_guard do
|
|
32
|
+
accountant.activities.none? do |activity|
|
|
33
|
+
activity.effective_date == accountant.date && activity.type?(:interest)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class WaterfallingAccountant < Morty::Accountant
|
|
2
|
+
activity :issue do
|
|
3
|
+
entry :receivable, :cash, amount
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
waterfall :payment, limit: :cr, complete: true, entries: <<~END
|
|
7
|
+
cash receivable
|
|
8
|
+
cash payable
|
|
9
|
+
END
|
|
10
|
+
|
|
11
|
+
waterfall :refund, limit: :dr, complete: true, entries: <<~END
|
|
12
|
+
payable cash
|
|
13
|
+
receivable cash
|
|
14
|
+
END
|
|
15
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
puts "loading features/support/env"
|
|
2
|
+
|
|
3
|
+
require "timecop"
|
|
4
|
+
require_relative "../../spec/spec_helper"
|
|
5
|
+
|
|
6
|
+
Morty::Engine.load_seed
|
|
7
|
+
|
|
8
|
+
Before do
|
|
9
|
+
ActiveRecord::Base.connection.begin_transaction(joinable: false)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
After do
|
|
13
|
+
ActiveRecord::Base.connection.rollback_transaction
|
|
14
|
+
Timecop.return
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
require "morty/cucumber/steps"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Feature: Payment waterfalls
|
|
2
|
+
|
|
3
|
+
Background:
|
|
4
|
+
Given a waterfalling accountant
|
|
5
|
+
And a start date of today
|
|
6
|
+
|
|
7
|
+
Scenario: No limit
|
|
8
|
+
When I apply an issue activity for $1.00
|
|
9
|
+
|
|
10
|
+
Then I have these balances:
|
|
11
|
+
| cash | -1.00 |
|
|
12
|
+
| receivable | 1.00 |
|
|
13
|
+
|
|
14
|
+
Scenario: DR limit
|
|
15
|
+
When I apply these activities:
|
|
16
|
+
| issue | 1.00 |
|
|
17
|
+
| payment | 1.00 |
|
|
18
|
+
| payment | 2.00 |
|
|
19
|
+
|
|
20
|
+
Then I have these balances:
|
|
21
|
+
| cash | 2.00 |
|
|
22
|
+
| receivable | 0.00 |
|
|
23
|
+
| payable | -2.00 |
|
|
24
|
+
|
|
25
|
+
Scenario: CR limit
|
|
26
|
+
When I apply these activities:
|
|
27
|
+
| payment | 1.00 |
|
|
28
|
+
| refund | 1.00 |
|
|
29
|
+
| refund | 2.00 |
|
|
30
|
+
|
|
31
|
+
Then I have these balances:
|
|
32
|
+
| cash | -2.00 |
|
|
33
|
+
| receivable | 2.00 |
|
|
34
|
+
| payable | 0.00 |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# This file was generated by Appraisal
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
gem "rails"
|
|
6
|
+
gem "lookup_by", github: "companygardener/lookup_by"
|
|
7
|
+
gem "rubocop-rails-omakase", require: false
|
|
8
|
+
gem "appraisal", "~> 2.5.0"
|
|
9
|
+
gem "activerecord", "~> 7.0.0"
|
|
10
|
+
gem "railties", "~> 7.0.0"
|
|
11
|
+
|
|
12
|
+
group :development, :test do
|
|
13
|
+
gem "rspec-rails"
|
|
14
|
+
gem "pg"
|
|
15
|
+
gem "puma"
|
|
16
|
+
gem "propshaft"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
group :test do
|
|
20
|
+
gem "rspec-its"
|
|
21
|
+
gem "factory_bot_rails"
|
|
22
|
+
gem "shoulda-matchers"
|
|
23
|
+
gem "cucumber"
|
|
24
|
+
gem "simplecov", require: false
|
|
25
|
+
gem "rspec_junit_formatter"
|
|
26
|
+
gem "timecop"
|
|
27
|
+
gem "chronic"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
gemspec path: "../"
|