From d39e7cea81f43b3aea16d81c9373e39f6a3bf2c3 Mon Sep 17 00:00:00 2001 From: Emi Matchu Date: Sun, 18 Feb 2024 20:29:31 -0800 Subject: [PATCH] Move fundraising models into the Fundraising module This was mostly straightforward it seems, whew! --- app/controllers/closet_hangers_controller.rb | 2 +- app/controllers/items_controller.rb | 4 +- app/controllers/outfits_controller.rb | 2 +- app/models/campaign.rb | 20 ------ app/models/donation.rb | 69 ------------------- app/models/donation_feature.rb | 24 ------- app/models/fundraising/campaign.rb | 22 ++++++ app/models/fundraising/donation.rb | 71 ++++++++++++++++++++ app/models/fundraising/donation_feature.rb | 26 +++++++ config/routes.rb | 4 +- db/seeds.rb | 2 +- 11 files changed, 126 insertions(+), 120 deletions(-) delete mode 100644 app/models/campaign.rb delete mode 100644 app/models/donation.rb delete mode 100644 app/models/donation_feature.rb create mode 100644 app/models/fundraising/campaign.rb create mode 100644 app/models/fundraising/donation.rb create mode 100644 app/models/fundraising/donation_feature.rb diff --git a/app/controllers/closet_hangers_controller.rb b/app/controllers/closet_hangers_controller.rb index da156252..fe3d33c4 100644 --- a/app/controllers/closet_hangers_controller.rb +++ b/app/controllers/closet_hangers_controller.rb @@ -65,7 +65,7 @@ class ClosetHangersController < ApplicationController current_user.assign_closeted_to_items!(items) end - @campaign = Campaign.current + @campaign = Fundraising::Campaign.current end def petpage diff --git a/app/controllers/items_controller.rb b/app/controllers/items_controller.rb index c102d9f0..6fd4df1a 100644 --- a/app/controllers/items_controller.rb +++ b/app/controllers/items_controller.rb @@ -18,7 +18,7 @@ class ItemsController < ApplicationController assign_closeted! respond_to do |format| format.html { - @campaign = Campaign.current rescue nil + @campaign = Fundraising::Campaign.current rescue nil if @items.count == 1 redirect_to @items.first else @@ -45,7 +45,7 @@ class ItemsController < ApplicationController else respond_to do |format| format.html { - @campaign = Campaign.current rescue nil + @campaign = Fundraising::Campaign.current rescue nil @newest_items = Item.newest.includes(:translations).limit(18) } format.js { render json: {error: '$q required'}} diff --git a/app/controllers/outfits_controller.rb b/app/controllers/outfits_controller.rb index 5b0d6c28..ca371574 100644 --- a/app/controllers/outfits_controller.rb +++ b/app/controllers/outfits_controller.rb @@ -76,7 +76,7 @@ class OutfitsController < ApplicationController @neopets_usernames = user_signed_in? ? current_user.neopets_usernames : [] - @campaign = Campaign.current rescue nil + @campaign = Fundraising::Campaign.current rescue nil end def show diff --git a/app/models/campaign.rb b/app/models/campaign.rb deleted file mode 100644 index c32bf8f5..00000000 --- a/app/models/campaign.rb +++ /dev/null @@ -1,20 +0,0 @@ -class Campaign < ApplicationRecord - has_many :donations - - def progress_percent - [(progress.to_f / goal) * 100, 100].min - end - - def remaining - goal - progress - end - - def complete? - progress >= goal - end - - def self.current - self.where(active: true).first or - raise ActiveRecord::RecordNotFound.new("no current campaign") - end -end diff --git a/app/models/donation.rb b/app/models/donation.rb deleted file mode 100644 index 7bee9df0..00000000 --- a/app/models/donation.rb +++ /dev/null @@ -1,69 +0,0 @@ -class Donation < ApplicationRecord - FEATURE_COST = 500 # in cents = $5.00 - - belongs_to :campaign - belongs_to :user, optional: true - has_many :features, class_name: 'DonationFeature' - - def to_param - "#{id}-#{secret}" - end - - def self.create_from_charge(campaign, user, params) - amount = (BigDecimal.new(params[:amount]) * 100).floor - - campaign.progress += amount - - charge_params = { - amount: amount, - description: 'Donation (thank you!)', - currency: 'usd' - } - - if params[:stripe_token_type] == 'card' - customer = Stripe::Customer.create( - card: params[:stripe_token] - ) - charge_params[:customer] = customer.id - elsif params[:stripe_token_type] == 'bitcoin_receiver' - charge_params[:card] = params[:stripe_token] - else - raise ArgumentError, "unexpected stripe token type #{params[:stripe_token_type]}" - end - - charge = Stripe::Charge.create(charge_params) - - donation = campaign.donations.build - donation.amount = amount - donation.charge_id = charge.id - donation.user = user - donation.donor_name = user.try(:name) - donation.donor_email = params[:donor_email] - donation.secret = new_secret - - num_features = amount / FEATURE_COST - features = [] - num_features.times do - features << donation.features.new - end - - Donation.transaction do - campaign.save! - donation.save! - features.each(&:save!) - end - - DonationMailer.thank_you_email(donation, donation.donor_email).deliver - - donation - end - - def self.new_secret - SecureRandom.urlsafe_base64 8 - end - - def self.from_param(param) - id, secret = param.split('-', 2) - self.where(secret: secret).find(id) - end -end diff --git a/app/models/donation_feature.rb b/app/models/donation_feature.rb deleted file mode 100644 index ac707c37..00000000 --- a/app/models/donation_feature.rb +++ /dev/null @@ -1,24 +0,0 @@ -class DonationFeature < ApplicationRecord - belongs_to :donation - belongs_to :outfit, optional: true - - validates :outfit, presence: true, if: :outfit_id_present? - - delegate :donor_name, to: :donation - - def as_json(options={}) - {donor_name: donor_name, outfit_image_url: outfit_image_url} - end - - def outfit_url=(outfit_url) - self.outfit_id = outfit_url.split('/').last rescue nil - end - - def outfit_id_present? - outfit_id.present? - end - - def outfit_image_url - outfit && outfit.image ? outfit.image.medium.url : nil - end -end diff --git a/app/models/fundraising/campaign.rb b/app/models/fundraising/campaign.rb new file mode 100644 index 00000000..3c748abe --- /dev/null +++ b/app/models/fundraising/campaign.rb @@ -0,0 +1,22 @@ +module Fundraising + class Campaign < ApplicationRecord + has_many :donations + + def progress_percent + [(progress.to_f / goal) * 100, 100].min + end + + def remaining + goal - progress + end + + def complete? + progress >= goal + end + + def self.current + self.where(active: true).first or + raise ActiveRecord::RecordNotFound.new("no current campaign") + end + end +end diff --git a/app/models/fundraising/donation.rb b/app/models/fundraising/donation.rb new file mode 100644 index 00000000..f304cd54 --- /dev/null +++ b/app/models/fundraising/donation.rb @@ -0,0 +1,71 @@ +module Fundraising + class Donation < ApplicationRecord + FEATURE_COST = 500 # in cents = $5.00 + + belongs_to :campaign + belongs_to :user, optional: true + has_many :features, class_name: 'DonationFeature' + + def to_param + "#{id}-#{secret}" + end + + def self.create_from_charge(campaign, user, params) + amount = (BigDecimal.new(params[:amount]) * 100).floor + + campaign.progress += amount + + charge_params = { + amount: amount, + description: 'Donation (thank you!)', + currency: 'usd' + } + + if params[:stripe_token_type] == 'card' + customer = Stripe::Customer.create( + card: params[:stripe_token] + ) + charge_params[:customer] = customer.id + elsif params[:stripe_token_type] == 'bitcoin_receiver' + charge_params[:card] = params[:stripe_token] + else + raise ArgumentError, "unexpected stripe token type #{params[:stripe_token_type]}" + end + + charge = Stripe::Charge.create(charge_params) + + donation = campaign.donations.build + donation.amount = amount + donation.charge_id = charge.id + donation.user = user + donation.donor_name = user.try(:name) + donation.donor_email = params[:donor_email] + donation.secret = new_secret + + num_features = amount / FEATURE_COST + features = [] + num_features.times do + features << donation.features.new + end + + Donation.transaction do + campaign.save! + donation.save! + features.each(&:save!) + end + + DonationMailer.thank_you_email(donation, donation.donor_email).deliver + + donation + end + + def self.new_secret + SecureRandom.urlsafe_base64 8 + end + + def self.from_param(param) + id, secret = param.split('-', 2) + self.where(secret: secret).find(id) + end + end +end diff --git a/app/models/fundraising/donation_feature.rb b/app/models/fundraising/donation_feature.rb new file mode 100644 index 00000000..2f55b80e --- /dev/null +++ b/app/models/fundraising/donation_feature.rb @@ -0,0 +1,26 @@ +module Fundraising + class DonationFeature < ApplicationRecord + belongs_to :donation + belongs_to :outfit, optional: true + + validates :outfit, presence: true, if: :outfit_id_present? + + delegate :donor_name, to: :donation + + def as_json(options={}) + {donor_name: donor_name, outfit_image_url: outfit_image_url} + end + + def outfit_url=(outfit_url) + self.outfit_id = outfit_url.split('/').last rescue nil + end + + def outfit_id_present? + outfit_id.present? + end + + def outfit_image_url + outfit && outfit.image ? outfit.image.medium.url : nil + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 7d0ec3e9..33dbbd0c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -79,15 +79,15 @@ OpenneoImpressItems::Application.routes.draw do end # Donation campaign stuff! - scope module: "fundraising" do + scope module: "fundraising", as: "fundraising" do resources :donations, only: [:create, :show, :update] do collection do resources :donation_features, path: 'features', only: [:index] end end resources :campaigns, only: [:show], path: '/donate/campaigns' - get '/donate' => 'campaigns#current', as: :donate end + get '/donate' => 'fundraising/campaigns#current', as: :donate # Static pages! get '/terms', as: :terms, diff --git a/db/seeds.rb b/db/seeds.rb index f06b1ad5..dc8c1f5f 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -209,4 +209,4 @@ Zone.create(:id => 52, :label => "Foreground", :plain_label => "foreground", :de # NOTE: Creating an AuthUser automatically creates a User, too. AuthUser.create(name: "test", password: "test123", email: "test@gmail.example") -Campaign.create(goal: 100_00, active: true, advertised: false, description: "") +Fundraising::Campaign.create(goal: 100_00, active: true, advertised: false, description: "")