Class: UsageCredits::Transaction
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- UsageCredits::Transaction
- Defined in:
- lib/usage_credits/models/transaction.rb
Overview
Records all credit changes in a wallet (additions, deductions, expirations).
Each transaction represents a single credit operation and includes:
- amount: How many credits (positive for additions, negative for deductions)
- category: What kind of operation (subscription fulfillment, pack purchase, etc)
- metadata: Additional details about the operation
- expires_at: When these credits expire (optional)
Constant Summary collapse
- DEFAULT_CATEGORIES =
Default transaction types, grouped by purpose:
[ # Bonus credits "signup_bonus", # Initial signup bonus "referral_bonus", # Referral reward bonus "bonus", # Generic bonus # Subscription-related "subscription_credits", # Generic subscription credits "subscription_trial", # Trial period credits "subscription_signup_bonus", # Bonus for subscribing "subscription_upgrade", # Plan upgrade credits # One-time purchases "credit_pack", # Generic credit pack "credit_pack_purchase", # Credit pack bought "credit_pack_refund", # Credit pack refunded # Credit usage & management "operation_charge", # Credits spent on operation "manual_adjustment", # Manual admin adjustment "credit_added", # Generic addition "credit_deducted" # Generic deduction ].freeze
- CATEGORIES =
Backwards compatibility: CATEGORIES constant still works but prefer using Transaction.categories for dynamic lookup
DEFAULT_CATEGORIES
Class Method Summary collapse
-
.categories ⇒ Array<String>
All valid categories: defaults + any custom categories added via config.
Instance Method Summary collapse
-
#allocated_amount ⇒ Object
How many credits from this transaction have already been allocated (spent)? Only applies if this transaction is positive.
-
#balance_after ⇒ Object
Get the balance after this transaction was applied Returns nil for transactions created before this feature was added.
-
#balance_before ⇒ Object
Get the balance before this transaction was applied Returns the stored value if available, otherwise nil Note: For transactions created before this feature, returns nil.
-
#credit? ⇒ Boolean
Is this transaction a positive credit or a negative (spend)?.
- #debit? ⇒ Boolean
-
#description ⇒ Object
Get a human-readable description of what this transaction represents.
-
#expired? ⇒ Boolean
Have these credits expired?.
-
#formatted_amount ⇒ Object
Format the amount for display (e.g., "+100 credits" or "-10 credits").
-
#formatted_balance_after ⇒ Object
Format the balance after for display (e.g., "500 credits") Returns nil if balance_after is not stored.
-
#metadata ⇒ Object
Get metadata with indifferent access (string/symbol keys) Returns empty hash if nil (for MySQL compatibility where JSON columns can't have defaults).
-
#metadata=(hash) ⇒ Object
Set metadata, ensuring consistent storage format.
-
#owner ⇒ Object
Get the owner of the wallet these credits belong to.
-
#reload ⇒ Object
Clear metadata cache on reload to ensure fresh data from database.
-
#remaining_amount ⇒ Object
How many credits remain unused in this positive transaction? If negative, this will effectively be 0.
Class Method Details
.categories ⇒ Array<String>
All valid categories: defaults + any custom categories added via config
45 46 47 |
# File 'lib/usage_credits/models/transaction.rb', line 45 def self.categories (DEFAULT_CATEGORIES + UsageCredits.configuration.additional_categories).uniq end |
Instance Method Details
#allocated_amount ⇒ Object
How many credits from this transaction have already been allocated (spent)? Only applies if this transaction is positive.
118 119 120 |
# File 'lib/usage_credits/models/transaction.rb', line 118 def allocated_amount incoming_allocations.sum(:amount) end |
#balance_after ⇒ Object
Get the balance after this transaction was applied Returns nil for transactions created before this feature was added
135 136 137 |
# File 'lib/usage_credits/models/transaction.rb', line 135 def balance_after [:balance_after] end |
#balance_before ⇒ Object
Get the balance before this transaction was applied Returns the stored value if available, otherwise nil Note: For transactions created before this feature, returns nil
142 143 144 |
# File 'lib/usage_credits/models/transaction.rb', line 142 def balance_before [:balance_before] end |
#credit? ⇒ Boolean
Is this transaction a positive credit or a negative (spend)?
108 109 110 |
# File 'lib/usage_credits/models/transaction.rb', line 108 def credit? amount > 0 end |
#debit? ⇒ Boolean
112 113 114 |
# File 'lib/usage_credits/models/transaction.rb', line 112 def debit? amount < 0 end |
#description ⇒ Object
Get a human-readable description of what this transaction represents
164 165 166 167 168 169 170 171 172 173 |
# File 'lib/usage_credits/models/transaction.rb', line 164 def description # Custom description takes precedence return self[:description] if self[:description].present? # Operation charges have dynamic descriptions return operation_description if category == "operation_charge" # Use predefined description or fallback to titleized category category.titleize end |
#expired? ⇒ Boolean
Have these credits expired?
103 104 105 |
# File 'lib/usage_credits/models/transaction.rb', line 103 def expired? expires_at.present? && expires_at < Time.current end |
#formatted_amount ⇒ Object
Format the amount for display (e.g., "+100 credits" or "-10 credits")
151 152 153 154 |
# File 'lib/usage_credits/models/transaction.rb', line 151 def formatted_amount prefix = amount.positive? ? "+" : "" "#{prefix}#{UsageCredits.configuration.credit_formatter.call(amount)}" end |
#formatted_balance_after ⇒ Object
Format the balance after for display (e.g., "500 credits") Returns nil if balance_after is not stored
158 159 160 161 |
# File 'lib/usage_credits/models/transaction.rb', line 158 def formatted_balance_after return nil unless balance_after UsageCredits.configuration.credit_formatter.call(balance_after) end |
#metadata ⇒ Object
Get metadata with indifferent access (string/symbol keys) Returns empty hash if nil (for MySQL compatibility where JSON columns can't have defaults)
184 185 186 |
# File 'lib/usage_credits/models/transaction.rb', line 184 def @indifferent_metadata ||= ActiveSupport::HashWithIndifferentAccess.new(super || {}) end |
#metadata=(hash) ⇒ Object
Set metadata, ensuring consistent storage format
189 190 191 192 |
# File 'lib/usage_credits/models/transaction.rb', line 189 def (hash) @indifferent_metadata = nil # Clear cache super(hash.is_a?(Hash) ? hash.to_h : {}) end |
#owner ⇒ Object
Get the owner of the wallet these credits belong to
98 99 100 |
# File 'lib/usage_credits/models/transaction.rb', line 98 def owner wallet.owner end |
#reload ⇒ Object
Clear metadata cache on reload to ensure fresh data from database
195 196 197 198 |
# File 'lib/usage_credits/models/transaction.rb', line 195 def reload(*) @indifferent_metadata = nil super end |
#remaining_amount ⇒ Object
How many credits remain unused in this positive transaction? If negative, this will effectively be 0.
124 125 126 127 |
# File 'lib/usage_credits/models/transaction.rb', line 124 def remaining_amount return 0 unless credit? amount - allocated_amount end |