1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-07-19 05:09:38 +02:00
Maybe/app/models/category.rb

130 lines
4.1 KiB
Ruby
Raw Normal View History

class Category < ApplicationRecord
has_many :transactions, dependent: :nullify, class_name: "Transaction"
has_many :import_mappings, as: :mappable, dependent: :destroy, class_name: "Import::Mapping"
belongs_to :family
has_many :budget_categories, dependent: :destroy
has_many :subcategories, class_name: "Category", foreign_key: :parent_id, dependent: :nullify
belongs_to :parent, class_name: "Category", optional: true
validates :name, :color, :lucide_icon, :family, presence: true
validates :name, uniqueness: { scope: :family_id }
validate :category_level_limit
validate :nested_category_matches_parent_classification
before_save :inherit_color_from_parent
scope :alphabetically, -> { order(:name) }
scope :roots, -> { where(parent_id: nil) }
scope :incomes, -> { where(classification: "income") }
scope :expenses, -> { where(classification: "expense") }
COLORS = %w[#e99537 #4da568 #6471eb #db5a54 #df4e92 #c44fe9 #eb5429 #61c9ea #805dee #6ad28a]
UNCATEGORIZED_COLOR = "#737373"
TRANSFER_COLOR = "#444CE7"
PAYMENT_COLOR = "#db5a54"
TRADE_COLOR = "#e99537"
class Group
attr_reader :category, :subcategories
delegate :name, :color, to: :category
def self.for(categories)
categories.select { |category| category.parent_id.nil? }.map do |category|
new(category, category.subcategories)
end
end
def initialize(category, subcategories = nil)
@category = category
@subcategories = subcategories || []
end
end
class << self
def icon_codes
%w[bus circle-dollar-sign ambulance apple award baby battery lightbulb bed-single beer bluetooth book briefcase building credit-card camera utensils cooking-pot cookie dices drama dog drill drum dumbbell gamepad-2 graduation-cap house hand-helping ice-cream-cone phone piggy-bank pill pizza printer puzzle ribbon shopping-cart shield-plus ticket trees]
end
Transaction rules engine V1 (#1900) * Domain model sketch * Scaffold out rules domain * Migrations * Remove existing data enrichment for clean slate * Sketch out business logic and basic tests * Simplify rule scope building and action executions * Get generator working again * Basic implementation + tests * Remove manual merchant management (rules will replace) * Revert "Remove manual merchant management (rules will replace)" This reverts commit 83dcbd9ff0aa7bbee211796b71aa48b71df5e57e. * Family and Provider merchants model * Fix brakeman warnings * Fix notification loader * Update notification position * Add Rule action and condition registries * Rule form with compound conditions and tests * Split out notification types, add CTA type * Rules form builder and Stimulus controller * Clean up rule registry domain * Clean up rules stimulus controller * CTA message for rule when user changes transaction category * Fix tests * Lint updates * Centralize notifications in Notifiable concern * Implement category rule prompts with auto backoff and option to disable * Fix layout bug caused by merge conflict * Initialize rule with correct action for category CTA * Add rule deletions, get rules working * Complete dynamic rule form, split Stimulus controllers by resource * Fix failing tests * Change test password to avoid chromium conflicts * Update integration tests * Centralize all test password references * Add re-apply rule action * Rule confirm modal * Run migrations * Trigger rule notification after inline category updates * Clean up rule styles * Basic attribute locking for rules * Apply attribute locks on user edits * Log data enrichments, only apply rules to unlocked attributes * Fix merge errors * Additional merge conflict fixes * Form UI improvements, ignore attribute locks on manual rule application * Batch AI auto-categorization of transactions * Auto merchant detection, ai enrichment in batches * Fix Plaid merchant assignments * Plaid category matching * Cleanup 1 * Test cleanup * Remove stale route * Fix desktop chat UI issues * Fix mobile nav styling issues
2025-04-18 11:39:58 -04:00
def bootstrap!
default_categories.each do |name, color, icon, classification|
find_or_create_by!(name: name) do |category|
category.color = color
Transaction rules engine V1 (#1900) * Domain model sketch * Scaffold out rules domain * Migrations * Remove existing data enrichment for clean slate * Sketch out business logic and basic tests * Simplify rule scope building and action executions * Get generator working again * Basic implementation + tests * Remove manual merchant management (rules will replace) * Revert "Remove manual merchant management (rules will replace)" This reverts commit 83dcbd9ff0aa7bbee211796b71aa48b71df5e57e. * Family and Provider merchants model * Fix brakeman warnings * Fix notification loader * Update notification position * Add Rule action and condition registries * Rule form with compound conditions and tests * Split out notification types, add CTA type * Rules form builder and Stimulus controller * Clean up rule registry domain * Clean up rules stimulus controller * CTA message for rule when user changes transaction category * Fix tests * Lint updates * Centralize notifications in Notifiable concern * Implement category rule prompts with auto backoff and option to disable * Fix layout bug caused by merge conflict * Initialize rule with correct action for category CTA * Add rule deletions, get rules working * Complete dynamic rule form, split Stimulus controllers by resource * Fix failing tests * Change test password to avoid chromium conflicts * Update integration tests * Centralize all test password references * Add re-apply rule action * Rule confirm modal * Run migrations * Trigger rule notification after inline category updates * Clean up rule styles * Basic attribute locking for rules * Apply attribute locks on user edits * Log data enrichments, only apply rules to unlocked attributes * Fix merge errors * Additional merge conflict fixes * Form UI improvements, ignore attribute locks on manual rule application * Batch AI auto-categorization of transactions * Auto merchant detection, ai enrichment in batches * Fix Plaid merchant assignments * Plaid category matching * Cleanup 1 * Test cleanup * Remove stale route * Fix desktop chat UI issues * Fix mobile nav styling issues
2025-04-18 11:39:58 -04:00
category.classification = classification
category.lucide_icon = icon
end
end
end
def uncategorized
new(
name: "Uncategorized",
color: UNCATEGORIZED_COLOR,
lucide_icon: "circle-dashed"
)
end
private
def default_categories
[
Transaction rules engine V1 (#1900) * Domain model sketch * Scaffold out rules domain * Migrations * Remove existing data enrichment for clean slate * Sketch out business logic and basic tests * Simplify rule scope building and action executions * Get generator working again * Basic implementation + tests * Remove manual merchant management (rules will replace) * Revert "Remove manual merchant management (rules will replace)" This reverts commit 83dcbd9ff0aa7bbee211796b71aa48b71df5e57e. * Family and Provider merchants model * Fix brakeman warnings * Fix notification loader * Update notification position * Add Rule action and condition registries * Rule form with compound conditions and tests * Split out notification types, add CTA type * Rules form builder and Stimulus controller * Clean up rule registry domain * Clean up rules stimulus controller * CTA message for rule when user changes transaction category * Fix tests * Lint updates * Centralize notifications in Notifiable concern * Implement category rule prompts with auto backoff and option to disable * Fix layout bug caused by merge conflict * Initialize rule with correct action for category CTA * Add rule deletions, get rules working * Complete dynamic rule form, split Stimulus controllers by resource * Fix failing tests * Change test password to avoid chromium conflicts * Update integration tests * Centralize all test password references * Add re-apply rule action * Rule confirm modal * Run migrations * Trigger rule notification after inline category updates * Clean up rule styles * Basic attribute locking for rules * Apply attribute locks on user edits * Log data enrichments, only apply rules to unlocked attributes * Fix merge errors * Additional merge conflict fixes * Form UI improvements, ignore attribute locks on manual rule application * Batch AI auto-categorization of transactions * Auto merchant detection, ai enrichment in batches * Fix Plaid merchant assignments * Plaid category matching * Cleanup 1 * Test cleanup * Remove stale route * Fix desktop chat UI issues * Fix mobile nav styling issues
2025-04-18 11:39:58 -04:00
[ "Income", "#e99537", "circle-dollar-sign", "income" ],
[ "Loan Payments", "#6471eb", "credit-card", "expense" ],
[ "Fees", "#6471eb", "credit-card", "expense" ],
[ "Entertainment", "#df4e92", "drama", "expense" ],
[ "Food & Drink", "#eb5429", "utensils", "expense" ],
[ "Shopping", "#e99537", "shopping-cart", "expense" ],
[ "Home Improvement", "#6471eb", "house", "expense" ],
[ "Healthcare", "#4da568", "pill", "expense" ],
[ "Personal Care", "#4da568", "pill", "expense" ],
[ "Services", "#4da568", "briefcase", "expense" ],
[ "Gifts & Donations", "#61c9ea", "hand-helping", "expense" ],
[ "Transportation", "#df4e92", "bus", "expense" ],
[ "Travel", "#df4e92", "plane", "expense" ],
[ "Rent & Utilities", "#db5a54", "lightbulb", "expense" ]
]
end
end
def inherit_color_from_parent
if subcategory?
self.color = parent.color
end
end
def replace_and_destroy!(replacement)
transaction do
transactions.update_all category_id: replacement&.id
destroy!
end
end
def parent?
subcategories.any?
end
def subcategory?
parent.present?
end
private
def category_level_limit
if (subcategory? && parent.subcategory?) || (parent? && subcategory?)
errors.add(:parent, "can't have more than 2 levels of subcategories")
end
end
def nested_category_matches_parent_classification
if subcategory? && parent.classification != classification
errors.add(:parent, "must have the same classification as its parent")
end
end
def monetizable_currency
family.currency
end
end