mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-19 05:09:38 +02:00
Vehicle view (#1117)
This commit is contained in:
parent
e856691c86
commit
359bceb58e
15 changed files with 259 additions and 8 deletions
42
app/controllers/vehicles_controller.rb
Normal file
42
app/controllers/vehicles_controller.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
class VehiclesController < ApplicationController
|
||||||
|
before_action :set_account, only: :update
|
||||||
|
|
||||||
|
def create
|
||||||
|
account = Current.family
|
||||||
|
.accounts
|
||||||
|
.create_with_optional_start_balance! \
|
||||||
|
attributes: account_params.except(:start_date, :start_balance),
|
||||||
|
start_date: account_params[:start_date],
|
||||||
|
start_balance: account_params[:start_balance]
|
||||||
|
|
||||||
|
account.sync_later
|
||||||
|
redirect_to account, notice: t(".success")
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@account.update!(account_params)
|
||||||
|
@account.sync_later
|
||||||
|
redirect_to @account, notice: t(".success")
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Current.family.accounts.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_params
|
||||||
|
params.require(:account)
|
||||||
|
.permit(
|
||||||
|
:name, :balance, :start_date, :start_balance, :currency, :accountable_type,
|
||||||
|
accountable_attributes: [
|
||||||
|
:id,
|
||||||
|
:make,
|
||||||
|
:model,
|
||||||
|
:year,
|
||||||
|
:mileage_value,
|
||||||
|
:mileage_unit
|
||||||
|
]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -29,6 +29,8 @@ module AccountsHelper
|
||||||
case account.accountable_type
|
case account.accountable_type
|
||||||
when "Property"
|
when "Property"
|
||||||
properties_path
|
properties_path
|
||||||
|
when "Vehicle"
|
||||||
|
vehicles_path
|
||||||
else
|
else
|
||||||
accounts_path
|
accounts_path
|
||||||
end
|
end
|
||||||
|
@ -38,6 +40,8 @@ module AccountsHelper
|
||||||
case account.accountable_type
|
case account.accountable_type
|
||||||
when "Property"
|
when "Property"
|
||||||
property_path(account)
|
property_path(account)
|
||||||
|
when "Vehicle"
|
||||||
|
vehicle_path(account)
|
||||||
else
|
else
|
||||||
account_path(account)
|
account_path(account)
|
||||||
end
|
end
|
||||||
|
@ -51,7 +55,7 @@ module AccountsHelper
|
||||||
transactions_tab = { key: "transactions", label: t("accounts.show.transactions"), path: account_path(account, tab: "transactions"), route: account_transactions_path(account) }
|
transactions_tab = { key: "transactions", label: t("accounts.show.transactions"), path: account_path(account, tab: "transactions"), route: account_transactions_path(account) }
|
||||||
trades_tab = { key: "trades", label: t("accounts.show.trades"), path: account_path(account, tab: "trades"), route: account_trades_path(account) }
|
trades_tab = { key: "trades", label: t("accounts.show.trades"), path: account_path(account, tab: "trades"), route: account_trades_path(account) }
|
||||||
|
|
||||||
return [ overview_tab, value_tab ] if account.property?
|
return [ overview_tab, value_tab ] if account.property? || account.vehicle?
|
||||||
return [ holdings_tab, cash_tab, trades_tab ] if account.investment?
|
return [ holdings_tab, cash_tab, trades_tab ] if account.investment?
|
||||||
|
|
||||||
[ value_tab, transactions_tab ]
|
[ value_tab, transactions_tab ]
|
||||||
|
|
2
app/helpers/vehicles_helper.rb
Normal file
2
app/helpers/vehicles_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module VehiclesHelper
|
||||||
|
end
|
|
@ -3,7 +3,7 @@ class Measurement
|
||||||
|
|
||||||
attr_reader :value, :unit
|
attr_reader :value, :unit
|
||||||
|
|
||||||
VALID_UNITS = %w[sqft sqm]
|
VALID_UNITS = %w[sqft sqm mi km]
|
||||||
|
|
||||||
validates :unit, inclusion: { in: VALID_UNITS }
|
validates :unit, inclusion: { in: VALID_UNITS }
|
||||||
validates :value, presence: true
|
validates :value, presence: true
|
||||||
|
|
|
@ -15,10 +15,6 @@ class Property < ApplicationRecord
|
||||||
first_valuation_amount
|
first_valuation_amount
|
||||||
end
|
end
|
||||||
|
|
||||||
def equity_value
|
|
||||||
account.balance_money
|
|
||||||
end
|
|
||||||
|
|
||||||
def trend
|
def trend
|
||||||
TimeSeries::Trend.new(current: account.balance_money, previous: first_valuation_amount)
|
TimeSeries::Trend.new(current: account.balance_money, previous: first_valuation_amount)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
class Vehicle < ApplicationRecord
|
class Vehicle < ApplicationRecord
|
||||||
include Accountable
|
include Accountable
|
||||||
|
|
||||||
|
attribute :mileage_unit, :string, default: "mi"
|
||||||
|
|
||||||
|
def mileage
|
||||||
|
Measurement.new(mileage_value, mileage_unit) if mileage_value.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def purchase_price
|
||||||
|
first_valuation_amount
|
||||||
|
end
|
||||||
|
|
||||||
|
def trend
|
||||||
|
TimeSeries::Trend.new(current: account.balance_money, previous: first_valuation_amount)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def first_valuation_amount
|
||||||
|
account.entries.account_valuations.order(:date).first&.amount_money || account.balance_money
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<%# locals: (f:) %>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<%= f.fields_for :accountable do |vehicle_form| %>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<%= vehicle_form.text_field :make, label: t(".make"), placeholder: t(".make_placeholder") %>
|
||||||
|
<%= vehicle_form.text_field :model, label: t(".model"), placeholder: t(".model_placeholder") %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<%= vehicle_form.text_field :year, label: t(".year"), placeholder: t(".year_placeholder") %>
|
||||||
|
<%= vehicle_form.text_field :mileage_value, label: t(".mileage"), placeholder: t(".mileage_placeholder") %>
|
||||||
|
<%= vehicle_form.select :mileage_unit,
|
||||||
|
[["Miles", "mi"], ["Kilometers", "km"]],
|
||||||
|
{ label: t(".mileage_unit") } %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
49
app/views/accounts/accountables/vehicle/_overview.html.erb
Normal file
49
app/views/accounts/accountables/vehicle/_overview.html.erb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<%# locals: (account:) %>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-3 gap-2">
|
||||||
|
<div class="rounded-xl bg-white shadow-xs border border-alpha-black-25 p-4">
|
||||||
|
<h4 class="text-gray-500 text-sm"><%= t(".make_model") %></h4>
|
||||||
|
<p class="text-xl font-medium text-gray-900">
|
||||||
|
<%= [account.vehicle.make, account.vehicle.model].compact.join(" ").presence || t(".unknown") %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-xl bg-white shadow-xs border border-alpha-black-25 p-4">
|
||||||
|
<h4 class="text-gray-500 text-sm"><%= t(".year") %></h4>
|
||||||
|
<p class="text-xl font-medium text-gray-900">
|
||||||
|
<%= account.vehicle.year || t(".unknown") %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-xl bg-white shadow-xs border border-alpha-black-25 p-4">
|
||||||
|
<h4 class="text-gray-500 text-sm flex items-center gap-1"><%= t(".mileage") %></h4>
|
||||||
|
<p class="text-xl font-medium text-gray-900">
|
||||||
|
<%= account.vehicle.mileage || t(".unknown") %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-xl bg-white shadow-xs border border-alpha-black-25 p-4">
|
||||||
|
<h4 class="text-gray-500 text-sm"><%= t(".purchase_price") %></h4>
|
||||||
|
<p class="text-xl font-medium text-gray-900">
|
||||||
|
<%= format_money account.vehicle.purchase_price %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-xl bg-white shadow-xs border border-alpha-black-25 p-4">
|
||||||
|
<h4 class="text-gray-500 text-sm"><%= t(".current_price") %></h4>
|
||||||
|
<p class="text-xl font-medium text-gray-900">
|
||||||
|
<%= format_money account.balance_money %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-xl bg-white shadow-xs border border-alpha-black-25 p-4">
|
||||||
|
<h4 class="text-gray-500 text-sm"><%= t(".trend") %></h4>
|
||||||
|
<div class="flex items-center gap-1" style="color: <%= account.vehicle.trend.color %>">
|
||||||
|
<p class="text-xl font-medium">
|
||||||
|
<%= account.vehicle.trend.value %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>(<%= account.vehicle.trend.percent %>%)</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -19,6 +19,24 @@ en:
|
||||||
postal_code: Postal code (optional)
|
postal_code: Postal code (optional)
|
||||||
state: State
|
state: State
|
||||||
year_built: Year built (optional)
|
year_built: Year built (optional)
|
||||||
|
vehicle:
|
||||||
|
make: Make
|
||||||
|
make_placeholder: Toyota
|
||||||
|
mileage: Mileage
|
||||||
|
mileage_placeholder: '15000'
|
||||||
|
mileage_unit: Unit
|
||||||
|
model: Model
|
||||||
|
model_placeholder: Camry
|
||||||
|
overview:
|
||||||
|
current_price: Current Price
|
||||||
|
make_model: Make & Model
|
||||||
|
mileage: Mileage
|
||||||
|
purchase_price: Purchase Price
|
||||||
|
trend: Trend
|
||||||
|
unknown: Unknown
|
||||||
|
year: Year
|
||||||
|
year: Year
|
||||||
|
year_placeholder: '2023'
|
||||||
create:
|
create:
|
||||||
success: New account created successfully
|
success: New account created successfully
|
||||||
destroy:
|
destroy:
|
||||||
|
|
7
config/locales/views/vehicles/en.yml
Normal file
7
config/locales/views/vehicles/en.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
en:
|
||||||
|
vehicles:
|
||||||
|
create:
|
||||||
|
success: Vehicle created successfully
|
||||||
|
update:
|
||||||
|
success: Vehicle updated successfully
|
|
@ -90,6 +90,7 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :properties, only: %i[ create update ]
|
resources :properties, only: %i[ create update ]
|
||||||
|
resources :vehicles, only: %i[ create update ]
|
||||||
|
|
||||||
resources :transactions, only: %i[ index new create ] do
|
resources :transactions, only: %i[ index new create ] do
|
||||||
collection do
|
collection do
|
||||||
|
|
9
db/migrate/20240823125526_add_details_to_vehicle.rb
Normal file
9
db/migrate/20240823125526_add_details_to_vehicle.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
class AddDetailsToVehicle < ActiveRecord::Migration[7.2]
|
||||||
|
def change
|
||||||
|
add_column :vehicles, :year, :integer
|
||||||
|
add_column :vehicles, :mileage_value, :integer
|
||||||
|
add_column :vehicles, :mileage_unit, :string
|
||||||
|
add_column :vehicles, :make, :string
|
||||||
|
add_column :vehicles, :model, :string
|
||||||
|
end
|
||||||
|
end
|
7
db/schema.rb
generated
7
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_08_22_180845) do
|
ActiveRecord::Schema[7.2].define(version: 2024_08_23_125526) 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"
|
||||||
|
@ -441,6 +441,11 @@ ActiveRecord::Schema[7.2].define(version: 2024_08_22_180845) do
|
||||||
create_table "vehicles", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
create_table "vehicles", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
||||||
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.integer "year"
|
||||||
|
t.integer "mileage_value"
|
||||||
|
t.string "mileage_unit"
|
||||||
|
t.string "make"
|
||||||
|
t.string "model"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_foreign_key "account_balances", "accounts", on_delete: :cascade
|
add_foreign_key "account_balances", "accounts", on_delete: :cascade
|
||||||
|
|
71
test/controllers/vehicles_controller_test.rb
Normal file
71
test/controllers/vehicles_controller_test.rb
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class VehiclesControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
setup do
|
||||||
|
sign_in @user = users(:family_admin)
|
||||||
|
@account = accounts(:vehicle)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "creates vehicle" do
|
||||||
|
assert_difference -> { Account.count } => 1,
|
||||||
|
-> { Vehicle.count } => 1,
|
||||||
|
-> { Account::Valuation.count } => 2,
|
||||||
|
-> { Account::Entry.count } => 2 do
|
||||||
|
post vehicles_path, params: {
|
||||||
|
account: {
|
||||||
|
name: "Vehicle",
|
||||||
|
balance: 30000,
|
||||||
|
currency: "USD",
|
||||||
|
accountable_type: "Vehicle",
|
||||||
|
start_date: 1.year.ago.to_date,
|
||||||
|
start_balance: 35000,
|
||||||
|
accountable_attributes: {
|
||||||
|
make: "Toyota",
|
||||||
|
model: "Camry",
|
||||||
|
year: 2020,
|
||||||
|
mileage_value: 15000,
|
||||||
|
mileage_unit: "mi"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
created_account = Account.order(:created_at).last
|
||||||
|
|
||||||
|
assert_equal "Toyota", created_account.vehicle.make
|
||||||
|
assert_equal "Camry", created_account.vehicle.model
|
||||||
|
assert_equal 2020, created_account.vehicle.year
|
||||||
|
assert_equal 15000, created_account.vehicle.mileage_value
|
||||||
|
assert_equal "mi", created_account.vehicle.mileage_unit
|
||||||
|
|
||||||
|
assert_redirected_to account_path(created_account)
|
||||||
|
assert_equal "Vehicle created successfully", flash[:notice]
|
||||||
|
assert_enqueued_with(job: AccountSyncJob)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates vehicle" do
|
||||||
|
assert_no_difference [ "Account.count", "Vehicle.count", "Account::Valuation.count", "Account::Entry.count" ] do
|
||||||
|
patch vehicle_path(@account), params: {
|
||||||
|
account: {
|
||||||
|
name: "Updated Vehicle",
|
||||||
|
balance: 28000,
|
||||||
|
currency: "USD",
|
||||||
|
accountable_type: "Vehicle",
|
||||||
|
accountable_attributes: {
|
||||||
|
id: @account.accountable_id,
|
||||||
|
make: "Honda",
|
||||||
|
model: "Accord",
|
||||||
|
year: 2021,
|
||||||
|
mileage_value: 20000,
|
||||||
|
mileage_unit: "mi",
|
||||||
|
purchase_price: 32000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_redirected_to account_path(@account)
|
||||||
|
assert_equal "Vehicle updated successfully", flash[:notice]
|
||||||
|
assert_enqueued_with(job: AccountSyncJob)
|
||||||
|
end
|
||||||
|
end
|
|
@ -34,7 +34,12 @@ class AccountsTest < ApplicationSystemTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
test "can create vehicle account" do
|
test "can create vehicle account" do
|
||||||
assert_account_created("Vehicle")
|
assert_account_created "Vehicle" do
|
||||||
|
fill_in "Make", with: "Toyota"
|
||||||
|
fill_in "Model", with: "Camry"
|
||||||
|
fill_in "Year", with: "2020"
|
||||||
|
fill_in "Mileage", with: "30000"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "can create other asset account" do
|
test "can create other asset account" do
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue