Merge branch 'csv'
Conflicts: app/models/user_certification.rb app/views/user_certifications/index.html.erb app/views/user_certifications/show.html.erb
This commit is contained in:
@@ -3,14 +3,20 @@ class Ability
|
||||
|
||||
def initialize(user)
|
||||
# Anonymous can read mac
|
||||
can :read, Mac
|
||||
today = Date.today
|
||||
event = Date.new(2013,9,1)
|
||||
|
||||
unless today == event
|
||||
can :read, Mac
|
||||
can :scan, Mac # Need anonymous so CRON can scan
|
||||
end
|
||||
|
||||
if !user.nil?
|
||||
|
||||
# By default, users can only see their own stuff
|
||||
can :read, Card, :user_id => user.id
|
||||
can :read, Certification
|
||||
can :read_details, Mac
|
||||
can :read_details, Mac unless today == event
|
||||
can [:update], Mac, :user_id => nil
|
||||
can [:create,:update], Mac, :user_id => user.id
|
||||
can :read, User, :id => user.id #TODO: why can users update themselves?
|
||||
@@ -29,9 +35,11 @@ class Ability
|
||||
can :read, UserCertification
|
||||
end
|
||||
|
||||
# Accountants can manage all
|
||||
# Accountants can manage payments
|
||||
if user.accountant?
|
||||
can :manage, Payment
|
||||
can :manage, Ipn
|
||||
can :manage, PaypalCsv
|
||||
end
|
||||
|
||||
# Admins can manage all
|
||||
|
||||
@@ -2,7 +2,8 @@ class Card < ActiveRecord::Base
|
||||
require 'open-uri'
|
||||
|
||||
attr_accessible :id, :user_id, :name, :card_number, :card_permissions
|
||||
validates_uniqueness_of :id,:card_number
|
||||
validates_presence_of :user_id, :card_number, :card_permissions
|
||||
validates_uniqueness_of :id, :card_number
|
||||
belongs_to :user
|
||||
|
||||
def upload_to_door
|
||||
|
||||
95
app/models/ipn.rb
Normal file
95
app/models/ipn.rb
Normal file
@@ -0,0 +1,95 @@
|
||||
require 'net/http'
|
||||
class Ipn < ActiveRecord::Base
|
||||
attr_accessible :data
|
||||
belongs_to :payment
|
||||
|
||||
after_create :create_payment
|
||||
|
||||
def date_parsed
|
||||
begin
|
||||
Date.strptime(self.payment_date, "%H:%M:%S %b %e, %Y %Z")
|
||||
rescue
|
||||
Date.new
|
||||
end
|
||||
end
|
||||
|
||||
def self.new_from_dynamic_params(params)
|
||||
ipn = Ipn.new()
|
||||
|
||||
ipn.attributes.each do |c|
|
||||
unless params[c.first.to_sym].nil?
|
||||
ipn[c.first.to_sym] = params[c.first.to_sym]
|
||||
end
|
||||
end
|
||||
|
||||
return ipn
|
||||
end
|
||||
|
||||
# Post back to Paypal to make sure it's valid
|
||||
def validate!
|
||||
uri = URI.parse('https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate')
|
||||
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.open_timeout = 60
|
||||
http.read_timeout = 60
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||
http.use_ssl = true
|
||||
response = http.post(uri.request_uri, self.data,
|
||||
'Content-Length' => "#{self.data.size}",
|
||||
'User-Agent' => "Ruby on Rails"
|
||||
).body
|
||||
|
||||
unless ["VERIFIED", "INVALID"].include?(response)
|
||||
Rails.logger.error "Faulty paypal result: #{response}"
|
||||
return false
|
||||
end
|
||||
unless response == "VERIFIED"
|
||||
Rails.logger.error "Invalid IPN: #{response}"
|
||||
Rails.logger.error "Data: #{self.data}"
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def link_payment
|
||||
create_payment
|
||||
end
|
||||
|
||||
private
|
||||
def create_payment
|
||||
# find user by email, then by payee
|
||||
user = User.where("lower(email) = ?", self._from_email_address.downcase).first
|
||||
user = User.where("lower(payee) = ?", self._from_email_address.downcase).first if user.nil? && self._from_email_address.present?
|
||||
|
||||
# Only create payments if the IPN matches a member
|
||||
if user.present?
|
||||
# And is a payment (not a cancellation, etc)
|
||||
payment_types = ["subscr_payment","send_money"]
|
||||
if payment_types.include?(self.txn_type)
|
||||
# And a member level
|
||||
if User.member_levels[self.payment_gross.to_i].present?
|
||||
payment = Payment.new
|
||||
payment.date = Date.strptime(self.payment_date, "%H:%M:%S %b %e, %Y %Z")
|
||||
payment.user_id = user.id
|
||||
payment.amount = self.payment_gross
|
||||
if payment.save
|
||||
self.payment_id = payment.id
|
||||
self.save!
|
||||
else
|
||||
return [false, "Unable to link payment. Payment error: #{payment.errors.full_messages.first}"]
|
||||
end
|
||||
else
|
||||
return [false, "Unable to link payment. Couldn't find membership level '#{self.payment_gross.to_i}'."]
|
||||
end
|
||||
else
|
||||
return [false, "Unable to link payment. Transaction is a '#{self.txn_type}' instead of '#{payment_types.inspect}'."]
|
||||
end
|
||||
else
|
||||
return [false, "Unable to link payment. Couldn't find user/payee '#{self.payer_email}'."]
|
||||
end
|
||||
|
||||
return [true]
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,8 +1,10 @@
|
||||
class Payment < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
attr_accessible :date, :user_id, :created_by
|
||||
has_one :ipn
|
||||
has_one :paypal_csv
|
||||
attr_accessible :date, :user_id, :created_by, :amount
|
||||
|
||||
validates_presence_of :user_id, :date, :created_by
|
||||
validates_presence_of :user_id, :date, :amount # not created_by
|
||||
validates_uniqueness_of :date, :scope => :user_id, :message => ' of payment already exists for this user.'
|
||||
|
||||
def human_date
|
||||
|
||||
73
app/models/paypal_csv.rb
Normal file
73
app/models/paypal_csv.rb
Normal file
@@ -0,0 +1,73 @@
|
||||
require 'csv'
|
||||
class PaypalCsv < ActiveRecord::Base
|
||||
attr_accessible :data, :_address_status, :_counterparty_status, :_currency, :_fee, :_from_email_address, :_gross, :_item_id, :_item_title, :_name, :_net, :_status, :_time, :_time_zone, :_to_email_address, :_transaction_id, :_type, :date, :string
|
||||
belongs_to :payment
|
||||
|
||||
after_create :create_payment
|
||||
|
||||
def date_parsed
|
||||
begin
|
||||
Date.strptime(self._date, "%m/%d/%Y")
|
||||
rescue
|
||||
Date.new
|
||||
end
|
||||
end
|
||||
|
||||
def self.batch_import_from_csv(filename)
|
||||
csv = CSV.table(filename)
|
||||
csv.each do |row|
|
||||
paypal_csv = PaypalCsv.new()
|
||||
|
||||
paypal_csv.attributes.each do |c|
|
||||
unless row[c.first.to_sym].nil?
|
||||
paypal_csv[c.first.to_sym] = row[c.first.to_sym]
|
||||
end
|
||||
end
|
||||
|
||||
paypal_csv.data = row.to_json
|
||||
paypal_csv.save
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def link_payment
|
||||
create_payment
|
||||
end
|
||||
|
||||
private
|
||||
def create_payment
|
||||
# find user by email, then by payee
|
||||
user = User.where("lower(email) = ?", self._from_email_address.downcase).first
|
||||
user = User.where("lower(payee) = ?", self._from_email_address.downcase).first if user.nil? && self._from_email_address.present?
|
||||
|
||||
# Only create payments if the CSV matches a member
|
||||
if user.present?
|
||||
# And is a payment (not a cancellation, etc)
|
||||
payment_types = ["Recurring Payment Received","Payment Received"]
|
||||
if payment_types.include?(self._type)
|
||||
# And a member level
|
||||
if User.member_levels[self._gross.to_i].present?
|
||||
payment = Payment.new
|
||||
payment.date = Date.strptime(self._date, "%m/%d/%Y") #7/6/2013 for Jul 06
|
||||
payment.user_id = user.id
|
||||
payment.amount = self._gross
|
||||
if payment.save
|
||||
self.payment_id = payment.id
|
||||
self.save!
|
||||
else
|
||||
return [false, "Unable to link payment. Payment error: #{payment.errors.full_messages.first}"]
|
||||
end
|
||||
else
|
||||
return [false, "Unable to link payment. Couldn't find membership level '#{self._gross.to_i}'."]
|
||||
end
|
||||
else
|
||||
return [false, "Unable to link payment. Transaction is a '#{self._type}' instead of '#{payment_types.inspect}'."]
|
||||
end
|
||||
else
|
||||
return [false, "Unable to link payment. Couldn't find user/payee '#{self._from_email_address}'."]
|
||||
end
|
||||
|
||||
return [true]
|
||||
end
|
||||
end
|
||||
@@ -85,72 +85,108 @@ class User < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def self.member_levels
|
||||
{25 => "Associate", 50 => "Basic", 75 => "Basic", 100 => "Plus"}
|
||||
end
|
||||
|
||||
def payment_status
|
||||
results = payment_status_calculation
|
||||
return results[:paid]
|
||||
end
|
||||
|
||||
def member_status
|
||||
case self.member_level.to_i
|
||||
when 0
|
||||
if self.payments.count > 0 then
|
||||
2
|
||||
else
|
||||
-1
|
||||
end
|
||||
when 1
|
||||
1
|
||||
when 10..24
|
||||
10
|
||||
when 25..999
|
||||
if self.payments.count > 0 then
|
||||
if self.payments.last.date < (DateTime.now - 45.days)
|
||||
3
|
||||
else
|
||||
case self.member_level.to_i
|
||||
when 25..49
|
||||
25
|
||||
when 50..99
|
||||
50
|
||||
when 100..999
|
||||
100
|
||||
end
|
||||
end
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
results = member_status_calculation
|
||||
return results[:rank]
|
||||
end
|
||||
|
||||
def member_status_symbol
|
||||
case self.member_level.to_i
|
||||
when 0
|
||||
if self.payments.count > 0 then
|
||||
"<span class='hoverinfo' title='Former Member (#{(DateTime.now - self.payments.last.date).to_i} days ago)'>:(</span>"
|
||||
else
|
||||
"<!-- Not a member -->"
|
||||
end
|
||||
when 1
|
||||
"Unable"
|
||||
when 10..24
|
||||
"<span class='hoverinfo' title='Volunteer'>◔</span>"
|
||||
when 25..999
|
||||
if self.payments.count > 0 then
|
||||
if self.payments.last.date < (DateTime.now - 45.days)
|
||||
"<span class='hoverinfo' title='Recently Lapsed (#{(DateTime.now - self.payments.last.date).to_i} days ago)'>◌</span>"
|
||||
else
|
||||
case self.member_level.to_i
|
||||
when 25..49
|
||||
"<span class='hoverinfo' title='#{member_level_string}'>◑</span>"
|
||||
when 50..99
|
||||
"<span class='hoverinfo' title='#{member_level_string}'>◕</span>"
|
||||
when 100..999
|
||||
"<span class='hoverinfo' title='#{member_level_string}'>●</span>"
|
||||
end
|
||||
end
|
||||
else
|
||||
"<span class='hoverinfo' title='No Payments'>?</span>"
|
||||
end
|
||||
results = member_status_calculation
|
||||
return "<img src='/#{results[:icon]}#{results[:flair]}-coin.png' title='#{results[:message]}' />"
|
||||
end
|
||||
|
||||
def delinquency
|
||||
if self.payments.count > 0
|
||||
paydate = self.payments.maximum(:date)
|
||||
(Date.today - paydate).to_i
|
||||
else
|
||||
(Date.today - self.created_at.to_date).to_i
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def member_status_calculation
|
||||
# Begin output buffer
|
||||
message = ""
|
||||
icon = ""
|
||||
flair = ""
|
||||
rank = 0
|
||||
|
||||
# First status item is level
|
||||
case self.member_level.to_i
|
||||
when 0..9
|
||||
if self.payments.count > 0 then
|
||||
message = "Former Member (#{(DateTime.now - self.payments.maximum(:date)).to_i/30} months ago)"
|
||||
icon = :timeout
|
||||
rank = 1
|
||||
else
|
||||
message = "Not a Member"
|
||||
icon = :no
|
||||
rank = 0
|
||||
end
|
||||
when 10..24
|
||||
message = "Volunteer"
|
||||
icon = :heart
|
||||
rank = 101
|
||||
when 25..49
|
||||
message = member_level_string
|
||||
icon = :copper
|
||||
rank = 250
|
||||
when 50..99
|
||||
message = member_level_string
|
||||
icon = :silver
|
||||
rank = 500
|
||||
when 100..999
|
||||
message = member_level_string
|
||||
icon = :gold
|
||||
rank = 1000
|
||||
end
|
||||
|
||||
payment_results = payment_status_calculation
|
||||
flair = payment_results[:flair]
|
||||
rank = rank/10 unless payment_results[:paid]
|
||||
message = payment_results[:message] unless payment_results[:message].blank?
|
||||
|
||||
return {:message => message, :icon => icon, :flair => flair, :rank => rank}
|
||||
end
|
||||
|
||||
def payment_status_calculation
|
||||
flair = ""
|
||||
message = ""
|
||||
paid = true
|
||||
|
||||
# Second status item is payment status
|
||||
case self.member_level.to_i
|
||||
when 25..999
|
||||
# There are payments
|
||||
if self.payments.count > 0 then
|
||||
# They're on time
|
||||
if self.payments.maximum(:date) > (DateTime.now - 60.days)
|
||||
flair = "-paid"
|
||||
paid = true
|
||||
else
|
||||
message = "Last Payment #{(DateTime.now - self.payments.maximum(:date)).to_i/30} months ago"
|
||||
paid = false
|
||||
end
|
||||
else
|
||||
message = "No Payments Recorded"
|
||||
paid = false
|
||||
end
|
||||
end
|
||||
return {:message => message, :paid => paid, :flair => flair}
|
||||
end
|
||||
|
||||
|
||||
def send_new_user_email
|
||||
Rails.logger.info UserMailer.new_user_email(self).deliver
|
||||
end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class UserCertification < ActiveRecord::Base
|
||||
attr_accessible :certification_id, :user_id
|
||||
|
||||
validates_presence_of :certification_id, :user_id
|
||||
validates_uniqueness_of :certification_id, :scope => :user_id, :message => 'already exists for this user.' # Makes sure users don't get certified twice
|
||||
|
||||
belongs_to :user
|
||||
|
||||
Reference in New Issue
Block a user