mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-19 13:19:39 +02:00
Integrate money-rails gem (#268)
* Integrate money-rails gem * Fix Lint issues * Basic test for monetize feature * Merge main branch * Changes to monetize balance field --------- Co-authored-by: Zach Gollwitzer <zach.gollwitzer@gmail.com>
This commit is contained in:
parent
323a8ccf50
commit
1182ab39bd
12 changed files with 176 additions and 14 deletions
1
Gemfile
1
Gemfile
|
@ -27,6 +27,7 @@ gem "bcrypt", "~> 3.1.7"
|
||||||
gem "inline_svg"
|
gem "inline_svg"
|
||||||
gem "jbuilder"
|
gem "jbuilder"
|
||||||
gem "tzinfo-data", platforms: %i[ windows jruby ]
|
gem "tzinfo-data", platforms: %i[ windows jruby ]
|
||||||
|
gem "money-rails", "~> 1.12"
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem "debug", platforms: %i[ mri windows ]
|
gem "debug", platforms: %i[ mri windows ]
|
||||||
|
|
10
Gemfile.lock
10
Gemfile.lock
|
@ -202,6 +202,15 @@ GEM
|
||||||
matrix (0.4.2)
|
matrix (0.4.2)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
minitest (5.21.2)
|
minitest (5.21.2)
|
||||||
|
monetize (1.13.0)
|
||||||
|
money (~> 6.12)
|
||||||
|
money (6.16.0)
|
||||||
|
i18n (>= 0.6.4, <= 2)
|
||||||
|
money-rails (1.15.0)
|
||||||
|
activesupport (>= 3.0)
|
||||||
|
monetize (~> 1.9)
|
||||||
|
money (~> 6.13)
|
||||||
|
railties (>= 3.0)
|
||||||
msgpack (1.7.2)
|
msgpack (1.7.2)
|
||||||
net-imap (0.4.10)
|
net-imap (0.4.10)
|
||||||
date
|
date
|
||||||
|
@ -382,6 +391,7 @@ DEPENDENCIES
|
||||||
inline_svg
|
inline_svg
|
||||||
jbuilder
|
jbuilder
|
||||||
letter_opener
|
letter_opener
|
||||||
|
money-rails (~> 1.12)
|
||||||
pg (~> 1.1)
|
pg (~> 1.1)
|
||||||
propshaft
|
propshaft
|
||||||
puma (>= 5.0)
|
puma (>= 5.0)
|
||||||
|
|
|
@ -30,7 +30,7 @@ class AccountsController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def account_params
|
def account_params
|
||||||
params.require(:account).permit(:name, :accountable_type, :balance, :subtype)
|
params.require(:account).permit(:name, :accountable_type, :balance, :balance_cents, :subtype)
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_type_class
|
def account_type_class
|
||||||
|
|
|
@ -4,4 +4,6 @@ class Account < ApplicationRecord
|
||||||
delegated_type :accountable, types: %w[ Account::Credit Account::Depository Account::Investment Account::Loan Account::OtherAsset Account::OtherLiability Account::Property Account::Vehicle], dependent: :destroy
|
delegated_type :accountable, types: %w[ Account::Credit Account::Depository Account::Investment Account::Loan Account::OtherAsset Account::OtherLiability Account::Property Account::Vehicle], dependent: :destroy
|
||||||
|
|
||||||
delegate :type_name, to: :accountable
|
delegate :type_name, to: :accountable
|
||||||
|
|
||||||
|
monetize :balance_cents
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<%= account.accountable %>
|
<%= account.accountable %>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-sm text-right">
|
<p class="text-sm text-right">
|
||||||
<span class="block mb-1"><%= number_to_currency account.balance %></span>
|
<span class="block mb-1"><%= humanized_money_with_symbol account.balance %></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
|
<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
|
||||||
<label for="account_balance" class="block text-sm font-medium opacity-50 focus-within:opacity-100">Balance</label>
|
<label for="account_balance" class="block text-sm font-medium opacity-50 focus-within:opacity-100">Balance</label>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<%= f.number_field :balance, placeholder: "$0.00", in: 0.00..100000000.00, required: 'required', class: "p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
|
<%= f.number_field :balance, placeholder: "$0.00", in: 0.00..100000000.00, step: :any, required: 'required', class: "p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
<%= account.name %>
|
<%= account.name %>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-sm text-right">
|
<p class="text-sm text-right">
|
||||||
<span class="block mb-1"><%= number_to_currency account.balance %></span>
|
<span class="block mb-1"><%= humanized_money_with_symbol account.balance %></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
114
config/initializers/money.rb
Normal file
114
config/initializers/money.rb
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
# encoding : utf-8
|
||||||
|
|
||||||
|
MoneyRails.configure do |config|
|
||||||
|
# To set the default currency
|
||||||
|
#
|
||||||
|
config.default_currency = :usd
|
||||||
|
|
||||||
|
# Set default bank object
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# config.default_bank = EuCentralBank.new
|
||||||
|
|
||||||
|
# Add exchange rates to current money bank object.
|
||||||
|
# (The conversion rate refers to one direction only)
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# config.add_rate "USD", "CAD", 1.24515
|
||||||
|
# config.add_rate "CAD", "USD", 0.803115
|
||||||
|
|
||||||
|
# To handle the inclusion of validations for monetized fields
|
||||||
|
# The default value is true
|
||||||
|
#
|
||||||
|
# config.include_validations = true
|
||||||
|
|
||||||
|
# Default ActiveRecord migration configuration values for columns:
|
||||||
|
#
|
||||||
|
# config.amount_column = { prefix: '', # column name prefix
|
||||||
|
# postfix: '_cents', # column name postfix
|
||||||
|
# column_name: nil, # full column name (overrides prefix, postfix and accessor name)
|
||||||
|
# type: :integer, # column type
|
||||||
|
# present: true, # column will be created
|
||||||
|
# null: false, # other options will be treated as column options
|
||||||
|
# default: 0
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# config.currency_column = { prefix: '',
|
||||||
|
# postfix: '_currency',
|
||||||
|
# column_name: nil,
|
||||||
|
# type: :string,
|
||||||
|
# present: true,
|
||||||
|
# null: false,
|
||||||
|
# default: 'USD'
|
||||||
|
# }
|
||||||
|
|
||||||
|
# Register a custom currency
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# config.register_currency = {
|
||||||
|
# priority: 1,
|
||||||
|
# iso_code: "EU4",
|
||||||
|
# name: "Euro with subunit of 4 digits",
|
||||||
|
# symbol: "€",
|
||||||
|
# symbol_first: true,
|
||||||
|
# subunit: "Subcent",
|
||||||
|
# subunit_to_unit: 10000,
|
||||||
|
# thousands_separator: ".",
|
||||||
|
# decimal_mark: ","
|
||||||
|
# }
|
||||||
|
|
||||||
|
# Specify a rounding mode
|
||||||
|
# Any one of:
|
||||||
|
#
|
||||||
|
# BigDecimal::ROUND_UP,
|
||||||
|
# BigDecimal::ROUND_DOWN,
|
||||||
|
# BigDecimal::ROUND_HALF_UP,
|
||||||
|
# BigDecimal::ROUND_HALF_DOWN,
|
||||||
|
# BigDecimal::ROUND_HALF_EVEN,
|
||||||
|
# BigDecimal::ROUND_CEILING,
|
||||||
|
# BigDecimal::ROUND_FLOOR
|
||||||
|
#
|
||||||
|
# set to BigDecimal::ROUND_HALF_EVEN by default
|
||||||
|
#
|
||||||
|
config.rounding_mode = BigDecimal::ROUND_HALF_UP
|
||||||
|
|
||||||
|
# Set default money format globally.
|
||||||
|
# Default value is nil meaning "ignore this option".
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# config.default_format = {
|
||||||
|
# no_cents_if_whole: nil,
|
||||||
|
# symbol: nil,
|
||||||
|
# sign_before_symbol: nil
|
||||||
|
# }
|
||||||
|
|
||||||
|
# If you would like to use I18n localization (formatting depends on the
|
||||||
|
# locale):
|
||||||
|
config.locale_backend = :i18n
|
||||||
|
#
|
||||||
|
# Example (using default localization from rails-i18n):
|
||||||
|
#
|
||||||
|
# I18n.locale = :en
|
||||||
|
# Money.new(10_000_00, 'USD').format # => $10,000.00
|
||||||
|
# I18n.locale = :es
|
||||||
|
# Money.new(10_000_00, 'USD').format # => $10.000,00
|
||||||
|
#
|
||||||
|
# For the legacy behaviour of "per currency" localization (formatting depends
|
||||||
|
# only on currency):
|
||||||
|
# config.locale_backend = :currency
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# Money.new(10_000_00, 'USD').format # => $10,000.00
|
||||||
|
# Money.new(10_000_00, 'EUR').format # => €10.000,00
|
||||||
|
#
|
||||||
|
# In case you don't need localization and would like to use default values
|
||||||
|
# (can be redefined using config.default_format):
|
||||||
|
# config.locale_backend = nil
|
||||||
|
|
||||||
|
# Set default raise_error_on_money_parsing option
|
||||||
|
# It will be raise error if assigned different currency
|
||||||
|
# The default value is false
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# config.raise_error_on_money_parsing = false
|
||||||
|
end
|
14
db/migrate/20240206031739_replace_money_field.rb
Normal file
14
db/migrate/20240206031739_replace_money_field.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
class ReplaceMoneyField < ActiveRecord::Migration[7.2]
|
||||||
|
def change
|
||||||
|
add_monetize :accounts, :balance
|
||||||
|
change_column :accounts, :balance_cents, :integer, limit: 8
|
||||||
|
|
||||||
|
Account.reset_column_information
|
||||||
|
|
||||||
|
Account.find_each do |account|
|
||||||
|
account.update_columns(balance_cents: Money.from_amount(account.balance, account.currency).cents)
|
||||||
|
end
|
||||||
|
|
||||||
|
remove_column :accounts, :balance
|
||||||
|
end
|
||||||
|
end
|
5
db/schema.rb
generated
5
db/schema.rb
generated
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.2].define(version: 2024_02_03_050018) do
|
ActiveRecord::Schema[7.2].define(version: 2024_02_06_031739) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pgcrypto"
|
enable_extension "pgcrypto"
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -59,12 +59,13 @@ ActiveRecord::Schema[7.2].define(version: 2024_02_03_050018) do
|
||||||
t.string "subtype"
|
t.string "subtype"
|
||||||
t.uuid "family_id", null: false
|
t.uuid "family_id", null: false
|
||||||
t.string "name"
|
t.string "name"
|
||||||
t.bigint "balance", default: 0
|
|
||||||
t.string "currency", default: "USD"
|
t.string "currency", default: "USD"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.string "accountable_type"
|
t.string "accountable_type"
|
||||||
t.uuid "accountable_id"
|
t.uuid "accountable_id"
|
||||||
|
t.bigint "balance_cents", default: 0, null: false
|
||||||
|
t.string "balance_currency", default: "USD", null: false
|
||||||
t.index ["accountable_type"], name: "index_accounts_on_accountable_type"
|
t.index ["accountable_type"], name: "index_accounts_on_accountable_type"
|
||||||
t.index ["family_id"], name: "index_accounts_on_family_id"
|
t.index ["family_id"], name: "index_accounts_on_family_id"
|
||||||
end
|
end
|
||||||
|
|
6
test/fixtures/accounts.yml
vendored
6
test/fixtures/accounts.yml
vendored
|
@ -1,14 +1,14 @@
|
||||||
dylan_checking:
|
dylan_checking:
|
||||||
family: dylan_family
|
family: dylan_family
|
||||||
name: Bob's Checking
|
name: Bob's Checking
|
||||||
balance: 1200
|
balance_cents: 1200
|
||||||
|
|
||||||
dylan_roth:
|
dylan_roth:
|
||||||
family: dylan_family
|
family: dylan_family
|
||||||
name: Bob's Roth IRA
|
name: Bob's Roth IRA
|
||||||
balance: 1200
|
balance_cents: 1200
|
||||||
|
|
||||||
richards_savings:
|
richards_savings:
|
||||||
family: richards_family
|
family: richards_family
|
||||||
name: Keef's HYSA
|
name: Keef's HYSA
|
||||||
balance: 1500
|
balance_cents: 1500
|
||||||
|
|
|
@ -1,11 +1,31 @@
|
||||||
require "test_helper"
|
require "test_helper"
|
||||||
|
|
||||||
class AccountTest < ActiveSupport::TestCase
|
class AccountTest < ActiveSupport::TestCase
|
||||||
test "new account should be valid" do
|
def setup
|
||||||
depository = Account::Depository.create!
|
depository = Account::Depository.create!
|
||||||
account = Account.create!(family: families(:dylan_family), name: "Explicit Checking", balance: 1200, accountable: depository)
|
@account = Account.create!(family: families(:dylan_family), name: "Explicit Checking", balance_cents: 1200, accountable: depository)
|
||||||
assert account.valid?
|
end
|
||||||
assert_not_nil account.accountable_id
|
|
||||||
assert_not_nil account.accountable
|
test "new account should be valid" do
|
||||||
|
assert @account.valid?
|
||||||
|
assert_not_nil @account.accountable_id
|
||||||
|
assert_not_nil @account.accountable
|
||||||
|
end
|
||||||
|
|
||||||
|
test "balance returns Money object" do
|
||||||
|
@account.balance = 10
|
||||||
|
assert_instance_of Money, @account.balance
|
||||||
|
assert_equal :usd, @account.balance.currency.id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "correctly assigns Money objects to the attribute" do
|
||||||
|
@account.balance = Money.new(2500, "USD")
|
||||||
|
assert_equal 2500, @account.balance_cents
|
||||||
|
end
|
||||||
|
|
||||||
|
test "balance_cents can be updated" do
|
||||||
|
new_balance = Money.new(10000, "USD")
|
||||||
|
@account.balance = new_balance
|
||||||
|
assert_equal new_balance, @account.balance
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue