Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions app/controllers/api/v1/accounts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def index

render json: {
error: "internal_server_error",
message: "An unexpected error occurred"
message: "Error: #{e.message}"
}, status: :internal_server_error
end

Expand All @@ -49,7 +49,7 @@ def show

render json: {
error: "internal_server_error",
message: "An unexpected error occurred"
message: "Error: #{e.message}"
}, status: :internal_server_error
end

Expand All @@ -63,10 +63,10 @@ def accounts_scope
scope = current_resource_owner.family.accounts
.accessible_by(current_resource_owner)
.includes(:accountable, account_providers: :provider)
include_disabled_accounts? ? scope : scope.visible
include_disabled_accounts? ? scope.historical : scope.visible
end

def include_disabled_accounts?
ActiveModel::Type::Boolean.new.cast(params[:include_disabled])
ActiveModel::Type::Boolean.new.cast(params[:include_disabled]) || false
end
end
12 changes: 11 additions & 1 deletion app/controllers/api/v1/balance_sheet_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ class Api::V1::BalanceSheetController < Api::V1::BaseController
# Returns net worth, total assets, and total liabilities as Money objects.
def show
family = current_resource_owner.family
balance_sheet = family.balance_sheet
balance_sheet = family.balance_sheet(
user: current_resource_owner,
include_disabled: include_disabled_accounts?
)

render json: {
currency: family.currency,
include_disabled: include_disabled_accounts?,
net_worth: balance_sheet.net_worth_money.as_json,
assets: balance_sheet.assets.total_money.as_json,
liabilities: balance_sheet.liabilities.total_money.as_json
Expand All @@ -24,4 +28,10 @@ def show
def ensure_read_scope
authorize_scope!(:read)
end

def include_disabled_accounts?
return @include_disabled_accounts if defined?(@include_disabled_accounts)

@include_disabled_accounts = ActiveModel::Type::Boolean.new.cast(params[:include_disabled]) || false
end
Comment thread
coderabbitai[bot] marked this conversation as resolved.
end
5 changes: 4 additions & 1 deletion app/controllers/api/v1/balances_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ def balances_scope
end

def accessible_account_ids
@accessible_account_ids ||= current_resource_owner.family.accounts.accessible_by(current_resource_owner).select(:id)
@accessible_account_ids ||= current_resource_owner.family.accounts
.accessible_by(current_resource_owner)
.historical
.select(:id)
end

def apply_filters(query)
Expand Down
32 changes: 26 additions & 6 deletions app/controllers/api/v1/holdings_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ class Api::V1::HoldingsController < Api::V1::BaseController
before_action :set_holding, only: [ :show ]

def index
family = current_resource_owner.family
holdings_query = family.holdings.joins(:account).where(accounts: { status: [ "draft", "active" ] })
holdings_query = holding_history_scope

holdings_query = apply_filters(holdings_query)
holdings_query = holdings_query.includes(:account, :security).chronological
Expand All @@ -21,6 +20,8 @@ def index
@per_page = safe_per_page_param

render :index
rescue InvalidFilterError => e
render_validation_error(e.message, [ e.message ])
rescue ArgumentError => e
render_validation_error(e.message, [ e.message ])
rescue => e
Expand All @@ -36,8 +37,9 @@ def show
private

def set_holding
family = current_resource_owner.family
@holding = family.holdings.joins(:account).where(accounts: { status: %w[draft active] }).find(params[:id])
raise ActiveRecord::RecordNotFound unless valid_uuid?(params[:id])

@holding = holding_history_scope.find(params[:id])
rescue ActiveRecord::RecordNotFound
render json: { error: "not_found", message: "Holding not found" }, status: :not_found
end
Expand All @@ -46,12 +48,30 @@ def ensure_read_scope
authorize_scope!(:read)
end

def holding_history_scope
account_ids = current_resource_owner.family.accounts
.accessible_by(current_resource_owner)
.historical
.select(:id)

current_resource_owner.family.holdings
.joins(:account)
.where(accounts: { id: account_ids })
end

def apply_filters(query)
if params[:account_id].present?
raise InvalidFilterError, "account_id must be a valid UUID" unless valid_uuid?(params[:account_id])

query = query.where(account_id: params[:account_id])
end
if params[:account_ids].present?
query = query.where(account_id: Array(params[:account_ids]))
account_ids = Array(params[:account_ids])
unless account_ids.all? { |account_id| valid_uuid?(account_id) }
raise InvalidFilterError, "account_ids must contain valid UUIDs"
end

query = query.where(account_id: account_ids)
end
if params[:date].present?
query = query.where(date: parse_date!(params[:date], "date"))
Expand Down Expand Up @@ -102,7 +122,7 @@ def log_and_render_error(action, exception)
Rails.logger.error exception.backtrace.join("\n")
render json: {
error: "internal_server_error",
message: "An unexpected error occurred"
message: "Error: #{exception.message}"
}, status: :internal_server_error
end
end
70 changes: 59 additions & 11 deletions app/controllers/api/v1/trades_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ class Api::V1::TradesController < Api::V1::BaseController

before_action :ensure_read_scope, only: [ :index, :show ]
before_action :ensure_write_scope, only: [ :create, :update, :destroy ]
before_action :set_trade, only: [ :show, :update, :destroy ]
before_action :set_readable_trade, only: :show
before_action :set_writable_trade, only: [ :update, :destroy ]

def index
family = current_resource_owner.family
trades_query = family.trades.visible
trades_query = trade_history_scope

trades_query = apply_filters(trades_query)
trades_query = trades_query.includes({ entry: :account }, :security, :category).reverse_chronological
Expand All @@ -22,6 +22,8 @@ def index
@per_page = safe_per_page_param

render :index
rescue InvalidFilterError => e
render_validation_error(e.message, [ e.message ])
rescue ArgumentError => e
render_validation_error(e.message, [ e.message ])
rescue => e
Expand All @@ -39,7 +41,10 @@ def create
return render_validation_error("Account ID is required", [ "Account ID is required" ])
end

account = current_resource_owner.family.accounts.visible.find(trade_params[:account_id])
account = current_resource_owner.family.accounts
.writable_by(current_resource_owner)
.visible
.find(trade_params[:account_id])

unless account.supports_trades?
return render_validation_error(
Expand Down Expand Up @@ -75,6 +80,8 @@ def create
rescue ActiveRecord::RecordNotFound => e
message = (e.model == "Account") ? "Account not found" : "Security not found"
render json: { error: "not_found", message: message }, status: :not_found
rescue ArgumentError => e
render_validation_error(e.message, [ e.message ])
rescue => e
log_and_render_error("create", e)
end
Expand All @@ -91,6 +98,8 @@ def update
else
render_validation_error("Trade could not be updated", @entry.errors.full_messages)
end
rescue ArgumentError => e
render_validation_error(e.message, [ e.message ])
rescue => e
log_and_render_error("update", e)
end
Expand All @@ -107,14 +116,34 @@ def destroy

private

def set_trade
family = current_resource_owner.family
@trade = family.trades.visible.find(params[:id])
def set_readable_trade
load_trade_with_account_scope(readable_trade_account_scope)
end

def set_writable_trade
load_trade_with_account_scope(writable_trade_account_scope)
end

def load_trade_with_account_scope(account_scope)
raise ActiveRecord::RecordNotFound unless valid_uuid?(params[:id])

@trade = current_resource_owner.family.trades
.joins(entry: :account)
.merge(account_scope)
.find(params[:id])
@entry = @trade.entry
rescue ActiveRecord::RecordNotFound
render json: { error: "not_found", message: "Trade not found" }, status: :not_found
end

def readable_trade_account_scope
Account.accessible_by(current_resource_owner).merge(Account.historical)
end

def writable_trade_account_scope
Account.writable_by(current_resource_owner).merge(Account.visible)
end

def ensure_read_scope
authorize_scope!(:read)
end
Expand All @@ -123,16 +152,34 @@ def ensure_write_scope
authorize_scope!(:write)
end

def trade_history_scope
account_ids = current_resource_owner.family.accounts
.accessible_by(current_resource_owner)
.historical
.select(:id)

current_resource_owner.family.trades
.joins(entry: :account)
.where(entries: { account_id: account_ids })
end

def apply_filters(query)
need_entry_join = params[:account_id].present? || params[:account_ids].present? ||
params[:start_date].present? || params[:end_date].present?
query = query.joins(:entry) if need_entry_join

if params[:account_id].present?
raise InvalidFilterError, "account_id must be a valid UUID" unless valid_uuid?(params[:account_id])

query = query.where(entries: { account_id: params[:account_id] })
end
if params[:account_ids].present?
query = query.where(entries: { account_id: Array(params[:account_ids]) })
account_ids = Array(params[:account_ids])
unless account_ids.all? { |account_id| valid_uuid?(account_id) }
raise InvalidFilterError, "account_ids must contain valid UUIDs"
end

query = query.where(entries: { account_id: account_ids })
end
if params[:start_date].present?
query = query.where("entries.date >= ?", parse_date!(params[:start_date], "start_date"))
Expand Down Expand Up @@ -161,7 +208,6 @@ def build_entry_params_for_update
flat = trade_update_params.to_h
entry_params = {
name: flat[:name],
date: flat[:date],
amount: flat[:amount],
currency: flat[:currency],
notes: flat[:notes],
Expand All @@ -172,6 +218,7 @@ def build_entry_params_for_update
category_id: flat[:category_id]
}.compact_blank
}.compact
entry_params[:date] = parse_date!(flat[:date], "date") if flat[:date].present?

original_qty = flat[:qty]
original_price = flat[:price]
Expand All @@ -183,6 +230,7 @@ def build_entry_params_for_update
is_sell = type_or_nature.present? ? trade_sell_from_type_or_nature?(type_or_nature) : @trade.qty.negative?
signed_qty = is_sell ? -qty.to_d.abs : qty.to_d.abs
entry_params[:entryable_attributes][:qty] = signed_qty
entry_params[:entryable_attributes][:price] = price.to_d if original_price.present?
entry_params[:amount] = signed_qty * price.to_d
ticker = @trade.security&.ticker
entry_params[:name] = Trade.build_name(is_sell ? "sell" : "buy", signed_qty.abs, ticker) if ticker.present?
Expand Down Expand Up @@ -242,7 +290,7 @@ def build_create_form_params(account)

{
account: account,
date: trade_params[:date],
date: parse_date!(trade_params[:date], "date"),
qty: qty,
price: price,
currency: trade_params[:currency].presence || account.currency,
Expand Down Expand Up @@ -292,7 +340,7 @@ def log_and_render_error(action, exception)
Rails.logger.error exception.backtrace.join("\n")
render json: {
error: "internal_server_error",
message: "An unexpected error occurred"
message: "Error: #{exception.message}"
}, status: :internal_server_error
end

Expand Down
Loading
Loading