1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-02 03:55:20 +02:00

Remove manual merchant management (rules will replace)

This commit is contained in:
Zach Gollwitzer 2025-04-02 17:48:34 -04:00
parent f07940bf45
commit 83dcbd9ff0
44 changed files with 123 additions and 386 deletions

View file

@ -1,71 +0,0 @@
module Account::Enrichable
extend ActiveSupport::Concern
def enrich_data
total_unenriched = entries.account_transactions
.joins("JOIN account_transactions at ON at.id = account_entries.entryable_id AND account_entries.entryable_type = 'Account::Transaction'")
.where("account_entries.enriched_at IS NULL OR at.merchant_id IS NULL OR at.category_id IS NULL")
.count
if total_unenriched > 0
batch_size = 50
batches = (total_unenriched.to_f / batch_size).ceil
batches.times do |batch|
EnrichTransactionBatchJob.perform_now(self, batch_size, batch * batch_size)
# EnrichTransactionBatchJob.perform_later(self, batch_size, batch * batch_size)
end
end
end
def enrich_transaction_batch(batch_size = 50, offset = 0)
transactions_batch = enrichable_transactions.offset(offset).limit(batch_size)
Rails.logger.info("Enriching batch of #{transactions_batch.count} transactions for account #{id} (offset: #{offset})")
merchants = {}
transactions_batch.each do |transaction|
begin
info = transaction.fetch_enrichment_info
next unless info.present?
if info.name.present?
merchant = merchants[info.name] ||= family.merchants.find_or_create_by(name: info.name)
if info.icon_url.present?
merchant.icon_url = info.icon_url
end
end
Account.transaction do
merchant.save! if merchant.present?
transaction.update!(merchant: merchant) if merchant.present? && transaction.merchant_id.nil?
transaction.entry.update!(
enriched_at: Time.current,
enriched_name: info.name,
)
end
rescue => e
Rails.logger.warn("Error enriching transaction #{transaction.id}: #{e.message}")
end
end
end
private
def enrichable?
family.data_enrichment_enabled? || (linked? && Rails.application.config.app_mode.hosted?)
end
def enrichable_transactions
transactions.active
.includes(:merchant, :category)
.where(
"account_entries.enriched_at IS NULL",
"OR merchant_id IS NULL",
"OR category_id IS NULL"
)
end
end

View file

@ -65,10 +65,7 @@ class Account::Entry < ApplicationRecord
bulk_attributes = {
date: bulk_update_params[:date],
notes: bulk_update_params[:notes],
entryable_attributes: {
category_id: bulk_update_params[:category_id],
merchant_id: bulk_update_params[:merchant_id]
}.compact_blank
entryable_attributes: { category_id: bulk_update_params[:category_id] }.compact_blank
}.compact_blank
return 0 if bulk_attributes.blank?

View file

@ -39,9 +39,9 @@ class Demo::Generator
ActiveRecord::Base.transaction do
create_tags!(family)
create_categories!(family)
create_merchants!(family)
create_merchants!
create_rules!(family)
puts "tags, categories, merchants created for #{family_name}"
puts "tags, categories, merchants created"
create_credit_card_account!(family)
create_checking_account!(family)
@ -213,13 +213,13 @@ class Demo::Generator
family.categories.create!(name: "Alcohol & Bars", parent: food, color: COLORS.sample, lucide_icon: "beer", classification: "expense")
end
def create_merchants!(family)
def create_merchants!
merchants = [ "Amazon", "Starbucks", "McDonald's", "Target", "Costco",
"Home Depot", "Shell", "Whole Foods", "Walgreens", "Nike",
"Uber", "Netflix", "Spotify", "Delta Airlines", "Airbnb", "Sephora" ]
merchants.each do |merchant|
family.merchants.create!(name: merchant, color: COLORS.sample)
Merchant.find_or_create_by_normalized_name!(merchant)
end
end

View file

@ -28,7 +28,6 @@ class Family < ApplicationRecord
has_many :tags, dependent: :destroy
has_many :categories, dependent: :destroy
has_many :merchants, dependent: :destroy
has_many :budgets, dependent: :destroy
has_many :budget_categories, through: :budgets
@ -36,6 +35,11 @@ class Family < ApplicationRecord
validates :locale, inclusion: { in: I18n.available_locales.map(&:to_s) }
validates :date_format, inclusion: { in: DATE_FORMATS.map(&:last) }
def merchants
transaction_merchant_ids = self.transactions.where.not(merchant_id: nil).pluck(:merchant_id).uniq
Merchant.where(id: transaction_merchant_ids)
end
def balance_sheet
@balance_sheet ||= BalanceSheet.new(self)
end

View file

@ -1,11 +1,24 @@
class Merchant < ApplicationRecord
has_many :transactions, dependent: :nullify, class_name: "Account::Transaction"
belongs_to :family
validates :name, :color, :family, presence: true
validates :name, uniqueness: { scope: :family }
validates :name, presence: true, uniqueness: true
scope :alphabetically, -> { order(:name) }
COLORS = %w[#e99537 #4da568 #6471eb #db5a54 #df4e92 #c44fe9 #eb5429 #61c9ea #805dee #6ad28a]
before_save :normalize_name
class << self
def normalize_name(name)
name.downcase.strip.titleize
end
def find_or_create_by_normalized_name!(name)
find_or_create_by!(name: normalize_name(name))
end
end
private
def normalize_name
self.name = self.class.normalize_name(name)
end
end

View file

@ -137,7 +137,7 @@ class PlaidAccount < ApplicationRecord
def get_merchant(plaid_merchant_name)
return nil if plaid_merchant_name.blank?
family.merchants.find_or_create_by!(name: plaid_merchant_name)
Merchant.find_or_create_by!(name: plaid_merchant_name)
end
def derive_plaid_cash_balance(plaid_balances)