Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transactions cleanup #817

Merged
merged 34 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
bd41211
Separate "modal" and "drawer" concepts for overlays
zachgoll May 23, 2024
7086c5a
Remove unused mobile pagination, set proper Turbo action
zachgoll May 24, 2024
7210c25
Add tests for pagination and transaction filtering
zachgoll May 24, 2024
54b28a9
Merge branch 'main' of github.com:maybe-finance/maybe into 795-transa…
zachgoll May 24, 2024
9cc34e3
Searchable concern
zachgoll May 27, 2024
5b03bfa
Revert "Searchable concern"
zachgoll May 27, 2024
565e6cb
Reorganize transaction partials part 1
zachgoll May 27, 2024
aab1978
Simplify search filters, temporarily remove Turbo
zachgoll May 27, 2024
e4f44cf
Simplify transaction searching, clean up controller
zachgoll May 27, 2024
7679594
Complete filter badges and add test
zachgoll May 28, 2024
bc1b04a
Reorganize transaction partials part 2
zachgoll May 28, 2024
ee41044
Merge branch 'main' of github.com:maybe-finance/maybe into 795-transa…
zachgoll May 28, 2024
fdaf044
Fix tests
zachgoll May 28, 2024
8f57916
Run system tests serially in CI
zachgoll May 28, 2024
c800ffb
Remove unused edit action
zachgoll May 29, 2024
2e7fdc7
Add back turbo
zachgoll May 29, 2024
91e9fcd
Clean up transaction model
zachgoll May 29, 2024
7d0ac15
Fix association ordering
zachgoll May 29, 2024
6d1a67c
Transaction controller cleanup part 3
zachgoll May 29, 2024
7f7a289
Rework sync
zachgoll May 29, 2024
2209fa1
Revert "Rework sync"
zachgoll May 29, 2024
e01057f
Tweaks to transaction model
zachgoll May 29, 2024
3080f21
Render transaction amount sign correctly
zachgoll May 29, 2024
9aeb10f
Add back simplified transaction turbo stream
zachgoll May 29, 2024
c41af41
Consolidate transaction partials and fix form update frame redirects
zachgoll May 30, 2024
5c29d95
Bump capybara wait time
zachgoll May 30, 2024
c96e32d
System test stabilization
zachgoll May 30, 2024
e5c102e
Remove redundant test
zachgoll May 30, 2024
6bca270
Don't enforce system tests yet for PRs
zachgoll May 30, 2024
14ac8a8
Add back test DB url
zachgoll May 30, 2024
ad7c280
Job env
zachgoll May 30, 2024
ddeaa0a
Rails env for action
zachgoll May 30, 2024
75a654c
Remove unused partial
zachgoll May 30, 2024
5797f76
Merge branch 'main' of github.com:maybe-finance/maybe into 795-transa…
zachgoll May 30, 2024
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
3 changes: 0 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ gem "turbo-rails"
# Background Jobs
gem "good_job"

# Search
gem "ransack", github: "maybe-finance/ransack", branch: "main"

# Error logging
gem "stackprof"
gem "sentry-ruby"
Expand Down
11 changes: 0 additions & 11 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,6 @@ GIT
lucide-rails (0.2.0)
railties (>= 4.1.0)

GIT
remote: https://github.com/maybe-finance/ransack.git
revision: dec20edc9ccccac77f5b4b8a1c1a9f20dc58fa04
branch: main
specs:
ransack (4.1.1)
activerecord (>= 6.1.5)
activesupport (>= 6.1.5)
i18n

GIT
remote: https://github.com/rails/rails.git
revision: c1f1b14adce5cd373ed63611486eb7a7db73c78c
Expand Down Expand Up @@ -494,7 +484,6 @@ DEPENDENCIES
puma (>= 5.0)
rails!
rails-settings-cached
ransack!
rubocop-rails-omakase
ruby-lsp-rails
selenium-webdriver
Expand Down
22 changes: 22 additions & 0 deletions app/controllers/transactions/rows_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Transactions::RowsController < ApplicationController
before_action :set_transaction, only: %i[ show update ]

def show
end

def update
@transaction.update! transaction_params

redirect_to transaction_row_path(@transaction)
end

private

def transaction_params
params.require(:transaction).permit(:category_id)
end

def set_transaction
@transaction = Current.family.transactions.find(params[:id])
end
end
126 changes: 18 additions & 108 deletions app/controllers/transactions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,15 @@ class TransactionsController < ApplicationController
before_action :set_transaction, only: %i[ show edit update destroy ]

def index
search_params = session[ransack_session_key] || params[:q]
@q = Current.family.transactions.ransack(search_params)
result = @q.result.order(date: :desc)
@pagy, @transactions = pagy(result, items: 10)
@q = search_params
result = Current.family.transactions.search(@q).ordered
@pagy, @transactions = pagy(result, items: 50)

@totals = {
count: result.count,
income: result.inflows.sum(&:amount_money).abs,
expense: result.outflows.sum(&:amount_money).abs
}
@filter_list, valid_params = Transaction.build_filter_list(search_params, Current.family)
session[ransack_session_key] = valid_params

respond_to do |format|
format.html
format.turbo_stream
end
end

def search
if params[:clear]
session.delete(ransack_session_key)
elsif params[:remove_param]
current_params = session[ransack_session_key] || {}
if params[:remove_param] == "date_range"
updated_params = current_params.except("date_gteq", "date_lteq")
elsif params[:remove_param_value]
key_to_remove = params[:remove_param]
value_to_remove = params[:remove_param_value]
updated_params = current_params.deep_dup
updated_params[key_to_remove] = updated_params[key_to_remove] - [ value_to_remove ]
else
updated_params = current_params.except(params[:remove_param])
end
session[ransack_session_key] = updated_params
elsif params[:q]
session[ransack_session_key] = params[:q]
end

index

respond_to do |format|
format.html { render :index }
format.turbo_stream do
render turbo_stream: [
turbo_stream.replace("transactions_summary", partial: "transactions/summary", locals: { totals: @totals }),
turbo_stream.replace("transactions_search_form", partial: "transactions/search_form", locals: { q: @q }),
turbo_stream.replace("transactions_filters", partial: "transactions/filters", locals: { filters: @filter_list }),
turbo_stream.replace("transactions_list", partial: "transactions/list", locals: { transactions: @transactions, pagy: @pagy })
]
end
end
end

def show
Expand All @@ -76,80 +34,28 @@ def create
.find(params[:transaction][:account_id])
.transactions.build(transaction_params.merge(amount: amount))

respond_to do |format|
if @transaction.save
@transaction.account.sync_later(@transaction.date)
format.html { redirect_to transactions_url, notice: t(".success") }
else
format.html { render :new, status: :unprocessable_entity }
end
end
@transaction.save!
@transaction.sync_account_later
redirect_to transactions_url, notice: t(".success")
end

def update
respond_to do |format|
sync_start_date = if transaction_params[:date]
[ @transaction.date, Date.parse(transaction_params[:date]) ].compact.min
else
@transaction.date
end

if params[:transaction][:tag_id].present?
tag = Current.family.tags.find(params[:transaction][:tag_id])
@transaction.tags << tag unless @transaction.tags.include?(tag)
end
@transaction.update! transaction_params
@transaction.sync_account_later

if params[:transaction][:remove_tag_id].present?
@transaction.tags.delete(params[:transaction][:remove_tag_id])
end

if @transaction.update(transaction_params)
@transaction.account.sync_later(sync_start_date)

format.html { redirect_to transaction_url(@transaction), notice: t(".success") }
format.turbo_stream do
render turbo_stream: [
turbo_stream.append("notification-tray", partial: "shared/notification", locals: { type: "success", content: { body: t(".success") } }),
turbo_stream.replace("transaction_#{@transaction.id}", partial: "transactions/transaction", locals: { transaction: @transaction })
]
end
else
format.html { render :edit, status: :unprocessable_entity }
end
end
redirect_to transaction_url(@transaction), notice: t(".success")
end

def destroy
@account = @transaction.account
sync_start_date = @account.transactions.where("date < ?", @transaction.date).order(date: :desc).first&.date
@transaction.destroy!
@account.sync_later(sync_start_date)

respond_to do |format|
format.html { redirect_to transactions_url, notice: t(".success") }
end
@transaction.sync_account_later
redirect_to transactions_url, notice: t(".success")
end

private

def delete_search_param(params, key, value: nil)
if value
params[key]&.delete(value)
params.delete(key) if params[key].empty? # Remove key if it's empty after deleting value
else
params.delete(key)
end

params
end

def ransack_session_key
:ransack_transactions_q
end

# Use callbacks to share common setup or constraints between actions.
def set_transaction
@transaction = Transaction.find(params[:id])
@transaction = Current.family.transactions.find(params[:id])
end

def amount
Expand All @@ -164,7 +70,11 @@ def nature
params[:transaction][:nature].to_s.inquiry
end

def search_params
params.fetch(:q, {}).permit(:start_date, :end_date, :search, accounts: [], account_ids: [], categories: [], merchants: [])
end

def transaction_params
params.require(:transaction).permit(:name, :date, :amount, :currency, :notes, :excluded, :category_id, :merchant_id, :tag_id, :remove_tag_id).except(:tag_id, :remove_tag_id)
params.require(:transaction).permit(:name, :date, :amount, :currency, :notes, :excluded, :category_id, :merchant_id, tag_ids: [], taggings_attributes: [ :id, :tag_id, :_destroy ])
end
end
28 changes: 21 additions & 7 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,37 @@ def notification(text, **options, &block)
render partial: "shared/notification", locals: { type: options[:type], content: { body: content } }
end

# Wrap view with <%= modal do %> ... <% end %> to have it open in a modal
# Make sure to add data-turbo-frame="modal" to the link/button that opens the modal
##
# Helper to open a centered and overlayed modal with custom contents
#
# @example Basic usage
# <%= modal classes: "custom-class" do %>
# <div>Content here</div>
# <% end %>
#
def modal(options = {}, &block)
content = capture &block
render partial: "shared/modal", locals: { content:, classes: options[:classes] }
end

##
# Helper to open a drawer on the right side of the screen with custom contents
#
# @example Basic usage
# <%= drawer do %>
# <div>Content here</div>
# <% end %>
#
def drawer(&block)
content = capture &block
render partial: "shared/drawer", locals: { content: content }
end

def account_groups(period: nil)
assets, liabilities = Current.family.accounts.by_group(currency: Current.family.currency, period: period || Period.last_30_days).values_at(:assets, :liabilities)
[ assets.children, liabilities.children ].flatten
end

def sidebar_modal(&block)
content = capture &block
render partial: "shared/sidebar_modal", locals: { content: content }
end

def sidebar_link_to(name, path, options = {})
is_current = current_page?(path) || (request.path.start_with?(path) && path != "/")

Expand Down
37 changes: 37 additions & 0 deletions app/helpers/transactions/searches_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module Transactions::SearchesHelper
def transaction_search_filters
[
{ key: "account_filter", name: "Account", icon: "layers" },
{ key: "date_filter", name: "Date", icon: "calendar" },
{ key: "type_filter", name: "Type", icon: "shapes" },
{ key: "amount_filter", name: "Amount", icon: "hash" },
{ key: "category_filter", name: "Category", icon: "tag" },
{ key: "merchant_filter", name: "Merchant", icon: "store" }
]
end

def get_transaction_search_filter_partial_path(filter)
"transactions/searches/filters/#{filter[:key]}"
end

def get_default_transaction_search_filter
transaction_search_filters[0]
end

def transactions_path_without_param(param_key, param_value)
updated_params = request.query_parameters.deep_dup

q_params = updated_params[:q] || {}

current_value = q_params[param_key]
if current_value.is_a?(Array)
q_params[param_key] = current_value - [ param_value ]
else
q_params.delete(param_key)
end

updated_params[:q] = q_params

transactions_path(updated_params)
end
end
32 changes: 14 additions & 18 deletions app/helpers/transactions_helper.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
module TransactionsHelper
def transaction_filters
[
{ name: "Account", partial: "account_filter", icon: "layers" },
{ name: "Date", partial: "date_filter", icon: "calendar" },
{ name: "Type", partial: "type_filter", icon: "shapes" },
{ name: "Amount", partial: "amount_filter", icon: "hash" },
{ name: "Category", partial: "category_filter", icon: "tag" },
{ name: "Merchant", partial: "merchant_filter", icon: "store" }
]
end
def transactions_group(date, transactions, transaction_partial_path = "transactions/transaction")
header_left = content_tag :span do
"#{date.strftime('%b %d, %Y')} · #{transactions.size}".html_safe
end

def transaction_filter_id(filter)
"txn-#{filter[:name].downcase}-filter"
end
header_right = content_tag :span do
format_money(-transactions.sum(&:amount_money))
end

def transaction_filter_by_name(name)
transaction_filters.find { |filter| filter[:name] == name }
end
header = header_left.concat(header_right)

content = render partial: transaction_partial_path, collection: transactions

def full_width_transaction_row?(route)
route != "/"
render partial: "shared/list_group", locals: {
header: header,
content: content
}
end
end
4 changes: 0 additions & 4 deletions app/models/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ class Account < ApplicationRecord

delegated_type :accountable, types: Accountable::TYPES, dependent: :destroy

def self.ransackable_attributes(auth_object = nil)
%w[name id]
end

def balance_on(date)
balances.where("date <= ?", date).order(date: :desc).first&.balance
end
Expand Down
Loading