1
0
Fork 0
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:
Zach Gollwitzer 2024-08-23 09:33:42 -04:00 committed by GitHub
parent e856691c86
commit 359bceb58e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 259 additions and 8 deletions

View 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

View file

@ -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 ]

View file

@ -0,0 +1,2 @@
module VehiclesHelper
end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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>

View 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>

View file

@ -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:

View file

@ -0,0 +1,7 @@
---
en:
vehicles:
create:
success: Vehicle created successfully
update:
success: Vehicle updated successfully

View file

@ -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

View 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
View file

@ -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

View 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

View file

@ -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