2024-06-20 08:15:09 -04:00
|
|
|
class Category < ApplicationRecord
|
2024-06-24 11:58:39 -04:00
|
|
|
has_many :transactions, dependent: :nullify, class_name: "Account::Transaction"
|
2024-10-01 10:47:59 -04:00
|
|
|
has_many :import_mappings, as: :mappable, dependent: :destroy, class_name: "Import::Mapping"
|
2024-12-20 11:37:26 -05:00
|
|
|
|
2024-03-07 19:15:50 +01:00
|
|
|
belongs_to :family
|
|
|
|
|
2024-12-20 11:37:26 -05:00
|
|
|
has_many :subcategories, class_name: "Category", foreign_key: :parent_id
|
|
|
|
belongs_to :parent, class_name: "Category", optional: true
|
|
|
|
|
2024-03-15 12:21:59 -07:00
|
|
|
validates :name, :color, :family, presence: true
|
2024-10-24 11:02:27 -04:00
|
|
|
validates :name, uniqueness: { scope: :family_id }
|
2024-03-15 12:21:59 -07:00
|
|
|
|
2024-12-20 11:37:26 -05:00
|
|
|
validate :category_level_limit
|
2024-03-07 19:15:50 +01:00
|
|
|
|
2024-04-16 12:44:31 -06:00
|
|
|
scope :alphabetically, -> { order(:name) }
|
|
|
|
|
2024-04-04 17:29:50 -04:00
|
|
|
COLORS = %w[#e99537 #4da568 #6471eb #db5a54 #df4e92 #c44fe9 #eb5429 #61c9ea #805dee #6ad28a]
|
|
|
|
|
|
|
|
UNCATEGORIZED_COLOR = "#737373"
|
2025-01-07 09:41:24 -05:00
|
|
|
TRANSFER_COLOR = "#444CE7"
|
|
|
|
PAYMENT_COLOR = "#db5a54"
|
|
|
|
TRADE_COLOR = "#e99537"
|
2024-04-04 17:29:50 -04:00
|
|
|
|
2024-12-20 11:37:26 -05:00
|
|
|
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 bootstrap_defaults
|
|
|
|
default_categories.each do |name, color|
|
|
|
|
find_or_create_by!(name: name) do |category|
|
|
|
|
category.color = color
|
|
|
|
end
|
|
|
|
end
|
2024-03-07 19:15:50 +01:00
|
|
|
end
|
|
|
|
|
2024-12-20 11:37:26 -05:00
|
|
|
private
|
|
|
|
def default_categories
|
|
|
|
[
|
|
|
|
[ "Income", "#e99537" ],
|
|
|
|
[ "Loan Payments", "#6471eb" ],
|
|
|
|
[ "Bank Fees", "#db5a54" ],
|
|
|
|
[ "Entertainment", "#df4e92" ],
|
|
|
|
[ "Food & Drink", "#c44fe9" ],
|
|
|
|
[ "Groceries", "#eb5429" ],
|
|
|
|
[ "Dining Out", "#61c9ea" ],
|
|
|
|
[ "General Merchandise", "#805dee" ],
|
|
|
|
[ "Clothing & Accessories", "#6ad28a" ],
|
|
|
|
[ "Electronics", "#e99537" ],
|
|
|
|
[ "Healthcare", "#4da568" ],
|
|
|
|
[ "Insurance", "#6471eb" ],
|
|
|
|
[ "Utilities", "#db5a54" ],
|
|
|
|
[ "Transportation", "#df4e92" ],
|
|
|
|
[ "Gas & Fuel", "#c44fe9" ],
|
|
|
|
[ "Education", "#eb5429" ],
|
|
|
|
[ "Charitable Donations", "#61c9ea" ],
|
|
|
|
[ "Subscriptions", "#805dee" ]
|
|
|
|
]
|
|
|
|
end
|
2024-03-07 19:15:50 +01:00
|
|
|
end
|
|
|
|
|
2024-05-02 07:24:31 -06:00
|
|
|
def replace_and_destroy!(replacement)
|
|
|
|
transaction do
|
|
|
|
transactions.update_all category_id: replacement&.id
|
|
|
|
destroy!
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-12-20 11:37:26 -05:00
|
|
|
def subcategory?
|
|
|
|
parent.present?
|
|
|
|
end
|
2024-03-07 19:15:50 +01:00
|
|
|
|
2024-12-20 11:37:26 -05:00
|
|
|
private
|
|
|
|
def category_level_limit
|
|
|
|
if subcategory? && parent.subcategory?
|
|
|
|
errors.add(:parent, "can't have more than 2 levels of subcategories")
|
|
|
|
end
|
2024-08-23 10:06:24 -04:00
|
|
|
end
|
2024-03-07 19:15:50 +01:00
|
|
|
end
|