mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-05 05:25:24 +02:00
Create tagging system (#792)
* Repro * Fix * Update signage * Create tagging system * Add tags to transaction imports * Build tagging UI * Cleanup * More cleanup
This commit is contained in:
parent
41c991384a
commit
457247da8e
38 changed files with 607 additions and 90 deletions
|
@ -1,5 +1,6 @@
|
|||
class Family < ApplicationRecord
|
||||
has_many :users, dependent: :destroy
|
||||
has_many :tags, dependent: :destroy
|
||||
has_many :accounts, dependent: :destroy
|
||||
has_many :transactions, through: :accounts
|
||||
has_many :imports, through: :accounts
|
||||
|
|
|
@ -111,16 +111,24 @@ class Import < ApplicationRecord
|
|||
def generate_transactions
|
||||
transactions = []
|
||||
category_cache = {}
|
||||
tag_cache = {}
|
||||
|
||||
csv.table.each do |row|
|
||||
category_name = row["category"]
|
||||
category_name = row["category"].presence
|
||||
tag_strings = row["tags"].presence&.split("|") || []
|
||||
tags = []
|
||||
|
||||
category = category_cache[category_name] ||= account.family.transaction_categories.find_or_initialize_by(name: category_name) if row["category"].present?
|
||||
tag_strings.each do |tag_string|
|
||||
tags << tag_cache[tag_string] ||= account.family.tags.find_or_initialize_by(name: tag_string)
|
||||
end
|
||||
|
||||
category = category_cache[category_name] ||= account.family.transaction_categories.find_or_initialize_by(name: category_name)
|
||||
|
||||
txn = account.transactions.build \
|
||||
name: row["name"].presence || FALLBACK_TRANSACTION_NAME,
|
||||
date: Date.iso8601(row["date"]),
|
||||
category: category,
|
||||
tags: tags,
|
||||
amount: BigDecimal(row["amount"]) * -1, # User inputs amounts with opposite signage of our internal representation
|
||||
currency: account.currency
|
||||
|
||||
|
@ -144,12 +152,16 @@ class Import < ApplicationRecord
|
|||
key: "category",
|
||||
label: "Category"
|
||||
|
||||
tags_field = Import::Field.new \
|
||||
key: "tags",
|
||||
label: "Tags"
|
||||
|
||||
amount_field = Import::Field.new \
|
||||
key: "amount",
|
||||
label: "Amount",
|
||||
validator: ->(value) { Import::Field.bigdecimal_validator(value) }
|
||||
|
||||
[ date_field, name_field, category_field, amount_field ]
|
||||
[ date_field, name_field, category_field, tags_field, amount_field ]
|
||||
end
|
||||
|
||||
def define_column_mapping_keys
|
||||
|
|
25
app/models/tag.rb
Normal file
25
app/models/tag.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
class Tag < ApplicationRecord
|
||||
belongs_to :family
|
||||
has_many :taggings, dependent: :destroy
|
||||
has_many :transactions, through: :taggings, source: :taggable, source_type: "Transaction"
|
||||
|
||||
validates :name, presence: true, uniqueness: { scope: :family }
|
||||
|
||||
scope :alphabetically, -> { order(:name) }
|
||||
|
||||
COLORS = %w[#e99537 #4da568 #6471eb #db5a54 #df4e92 #c44fe9 #eb5429 #61c9ea #805dee #6ad28a]
|
||||
|
||||
UNCATEGORIZED_COLOR = "#737373"
|
||||
|
||||
def replace_and_destroy!(replacement)
|
||||
transaction do
|
||||
raise ActiveRecord::RecordInvalid, "Replacement tag cannot be the same as the tag being destroyed" if replacement == self
|
||||
|
||||
if replacement
|
||||
taggings.update_all tag_id: replacement.id
|
||||
end
|
||||
|
||||
destroy!
|
||||
end
|
||||
end
|
||||
end
|
4
app/models/tagging.rb
Normal file
4
app/models/tagging.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
class Tagging < ApplicationRecord
|
||||
belongs_to :tag
|
||||
belongs_to :taggable, polymorphic: true
|
||||
end
|
|
@ -5,6 +5,9 @@ class Transaction < ApplicationRecord
|
|||
belongs_to :category, optional: true
|
||||
belongs_to :merchant, optional: true
|
||||
|
||||
has_many :taggings, as: :taggable, dependent: :destroy
|
||||
has_many :tags, through: :taggings
|
||||
|
||||
validates :name, :date, :amount, :account, presence: true
|
||||
|
||||
monetize :amount
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue