I want users to be able to redeem their accounts, after they purchase them through a redemption code.
class Code < ActiveRecord::Base
#redemption_codes = Account.where(:redeemed == false).map(&:redemption_code)
validates :code, inclusion: { in: #redemption_codes }
end
Here's my create method in my AccountsController
def create
#code = Code.new(code_params)
#account = Account.where(redemption_code: #code)
if #code.save
#account.redeemed == true
flash[:notice] = "Account Redeemed"
redirect_to root_path
else
flash[:danger] = "This does not match any accounts"
render 'new'
end
end
After a code is saved, a user entering a redemption code, I want the :redeemed value of the item to change from false to true.
I got an error, I have yet to see before
Undefined Method 'redeemed' for #<ActiveRecord::Relation[]>
Not sure why though
It's because when you call Account.where(redemption_code: #code), that may return multiple accounts. You need to do something like Account.where(redemption_code: #code).first or Account.find_by_redemption_code(#code).
Related
Post has_many :comments
Comment belongs_to :post
I want to bulk update (approve) all comments of posts that are not cancelled. If there is a cancelled comment selected, display the notification else approve comments.
def make_comments_approved
comment_ids = params[:comment_ids]
if comment_ids
posts = Post.includes(:comments).
where(comments: { id: comment_ids.keys })
comments = posts.comments
cancelled_comments = posts.
where(comments: { status: :cancelled }).comments
comments_to_update = comments - cancelled_comments
if cancelled_comments.any?
flash[:error] = "Cannot approve cancelled comments: #{cancelled_comments.pluck(:number)}"
else
approve_comments(comments_to_update)
flash[:notice] =
"#{pluralize(comments.count, 'comment')} successfully approved"
end
else
flash[:error] = 'Select comments to update'
end
redirect_to :back
end
private
def approve_comments(comments)
# approve here
end
Use update_all for bulk update. For example:
comments.update_all(approved: true)
However, be cautious as it does not trigger validations or callbacks.
If you have comment_ids with you do following
cancelled_comment = Comment.where(id: comment_ids.keys, status: :cancelled)
if cancelled_comment.count > 0
flash[:error] = "Cannot approve cancelled comments: #{cancelled_comments.pluck(:number)}"
else
comments = Comment.where(id: comment_ids.keys).update_all()
flash[:notice] = "#{pluralize(comments.count, 'comment')} successfully approved"
end
If you are using the search-logic gem, you can do the following (Syntax is of Rails 2)
Comment.status_ne("cancelled").update_all(:approved => true)
I am new to ruby/rails/spree. I am implementing Indian payment gateway with spree-3.0.7.
I am able to process the order but payment status is always at balance_due.
Controller code
def confirm
payment_method = Spree::PaymentMethod.find(payment_method_id)
Spree::LogEntry.create({
source: payment_method,
details: params.to_yaml
})
order = current_order || raise(ActiveRecord::RecordNotFound)
if(address = order.bill_address || order.ship_address)
firstname = address.firstname
end
#confirm for correct hash and order amount requested before marking an payment as 'complete'
checksum_matched = payment_method.checksum_ok?([params[:status], '', '', '', '', '', '', params[:udf4], params[:udf3], params[:udf2], params[:udf1], order.email, firstname, #productinfo, params[:amount], params[:txnid]], params[:hash])
if !checksum_matched
flash.alert = 'Malicious transaction detected.'
redirect_to checkout_state_path(order.state)
return
end
#check for order amount
if !payment_method.amount_ok?(order.total, params[:amount])
flash.alert = 'Malicious transaction detected. Order amount not matched.'
redirect_to checkout_state_path(order.state)
return
end
payment = order.payments.create!({
source_type: 'Spree::Gateway::Payumoney',#could be something generated by system
amount: order.total,
payment_method: payment_method
})
payment.started_processing!
payment.pend!
order.next
order.update_attributes({:state => "complete", :completed_at => Time.now})
if order.complete?
order.update!
flash.notice = Spree.t(:order_processed_successfully)
redirect_to order_path(order)
return
else
redirect_to checkout_state_path(order.state)
return
end
end
Gateway/Model Code
require "offsite_payments"
module Spree
class Gateway::Payumoney < Gateway
preference :merchant_id, :string
preference :secret_key, :string
def provider_class
::OffsitePayments.integration('Payu_In')
end
def provider
#assign payment mode
OffsitePayments.mode = preferred_test_mode == true ? :test : :production
provider_class
end
def checksum(items)
provider_class.checksum(preferred_merchant_id, preferred_secret_key, items)
end
def auto_capture?
true
end
def method_type
"payumoney"
end
def support?(source)
true
end
def authorization
self
end
def purchase(amount, source, gateway_options={})
ActiveMerchant::Billing::Response.new(true, "payumoney success")
end
def success?
true
end
def txnid(order)
order.id.to_s + order.number.to_s
end
def service_provider
"payu_paisa"
end
def checksum_ok?(itms, pg_hash)
Digest::SHA512.hexdigest([preferred_secret_key, *itms, preferred_merchant_id].join("|")) == pg_hash
end
def amount_ok?(order_total, pg_amount)
BigDecimal.new(pg_amount) == order_total
end
end
in spree payment doc https://guides.spreecommerce.com/developer/payments.html they have mentioned if auto_capture? return true then purchase method will be called but purchase method is not getting called.
Can anyone point me to right direction?
You need not call the following commands
payment.started_processing!
payment.pend!
Just leave the payment in its initial state. i.e. checkout state and complete your order.
Because when order is completed process_payments! is called.
This method processes unprocessed payments whose criteria is like below
def unprocessed_payments
payments.select { |payment| payment.checkout? }
end
Hope this solves your case :)
I fixed the issue by marking payment as complete.
remove
payment.started_processing!
payment.pend!
add
payment.complete
above
order.next
I have published my code at github as gem
https://github.com/isantoshsingh/spree_payumoney
I have three models: Employee, User, and Role. Relationship between these classes are employee --- one_to_one -- user and user -- one_to_many -- role.
My create action is working fine with following strong params methods
def employee_params
params.require(:employee).permit(:first_name, :middle_name, :last_name, :email, user_attributes: [:id, role_ids:[]])
end
For update if employee record has no user object, I am instantiating new user, mapping it to employee and calling update by passing string params. However, the update is failing with message:
Failed to save the new associated user
My update method code is
def update
#employee = Employee.find(params[:id])
if params[:employee][:user_attributes] != nil && params[:employee][:user_attributes][:role_ids] != nil && ! params[:employee][:user_attributes][:role_ids].empty?
if #employee.user == nil
#employee.user = User.new
temp_password = Devise.friendly_token.first(8)
#employee.user.is_temp_password = true
#employee.user.password = Devise.friendly_token.first(8)
#employee.user.email = #employee.email
#employee.user.email = params[:employee][:email] if params[:employee][:email]
end
end
respond_to do |format|
if #employee.update(employee_params)
format.json { render json: #employee.as_json}
else
format.json {render :json => #employee.errors.as_json, :status => 422}
end
end
end
I think as the above users suggested, you need to save the new User Object but also I think you should have the User creation code inside of the Employee create since you you would need to auto create it anyways in the update
Not sure if you also aware of helpers blank?, present? but I rewrote your code with that
def update
#employee = Employee.find(params[:id])
if params[:employee][:user_attributes].present? && params[:employee][:user_attributes][:role_ids].present? && params[:employee][:user_attributes][:role_ids].present?
unless #employee.user
user = User.new
#employee.user = user
temp_password = Devise.friendly_token.first(8)
user.is_temp_password = true
user.password = Devise.friendly_token.first(8)
user.email = #employee.email
user.email = params[:employee][:email] if params[:employee][:email]
user.save!
end
end
I have a table 'Likes' with columns business_id, user_id and liked(0,1) and a function 'change_like_status'.
Now on every function call, If the value is 1 then set it to 0 (or vice versa) and if record doesn't exists then create one with value 1.
The first_or_create method is working just fine but how can i toggle value of column 'liked' while using this method?
Here is my function:
def change_like_status
if current_user.present?
status = Like.where("business_id = ? AND user_id = ?",params['id'],current_user.id).first_or_create(:business_id => params['id'],:user_id => current_user.id,:liked => '1')
abort status.inspect
else
return render :json => {:status => false,:msg=>"You need to sign in before performing this action."}
end
end
In you controller, make the changes
def change_like_status
if current_user
status = Like.create_or_change_status(params[:id], current_user.id)
else
return render json: { status: false, msg: "You need to sign in before performing this action." }
end
end
In your model like.rb file, add a method
def self.create_or_change_status(business_id, user_id)
status = where(business_id: business_id, user_id: user_id).first
if status.nil?
status = create({business_id: business_id, user_id: user_id, liked: 1})
else
status.update_attributes(liked: !status.liked)
end
status
end
def change_like_status
if current_user
current_user.likes.find_by(business_id: params[:id]).switch_status!
else
return render json: { status: false, msg: "You need to sign in before performing this action." }
end
end
class Like
def switch_status!
self.update_column :liked, !liked
end
end
other approach should be something like that
class Like
def switch_status!
self.update_column :liked, !liked
end
end
class User
def likes id
likes_for_business id
end
def likes_for_business(id)
likes.find_by(business_id: id) || likes.create(:business_id: id, liked: true)
end
end
# controller
current_user.likes(params[:id]).switch_status!
I have the following model;
(app/models/student_inactivation_log.rb)
class StudentInactivationLog < ActiveRecord::Base
belongs_to :student
belongs_to :institution_user
belongs_to :period
validates_presence_of :student_id, :inactivated_on, :inactivation_reason
INACTIVATION_REASONS = [{ id: 1, short_name: "HTY", name: "You didn't study enough!"},
{ id: 2, short_name: "KS", name: "Graduated!"},
{ id: 3, short_name: "SBK",name: "Other Reason"}]
Class methods
class << self
def inactivation_reason_ids
INACTIVATION_REASONS.collect{|v| v[:id]}
end
def inactivation_reason_names
INACTIVATION_REASONS.collect{|v| v[:name]}
end
def inactivation_reason_name(id)
INACTIVATION_REASONS.select{|t| t[:id] == id}.first[:name]
end
def inactivation_reason_short_name(id)
INACTIVATION_REASONS.select{|t| t[:id] == id}.first[:short_name]
end
def inactivation_reason_id(name)
INACTIVATION_REASONS.select{|t| t[:name] == name}.first[:id]
end
end
# Instance methods
def inactivation_reason_name
self.class.inactivation_reason_name(self.inactivation_reason)
end
def inactivation_reason_short_name
self.class.inactivation_reason_short_name(self.inactivation_reason)
end
def inactivation_reason_id
self.class.inactivation_reason_id(self.inactivation_reason)
end
end
I would like to call these inactivation reasons from my controller, which is app/controllers/student/session_controllers.rb file:
class Student::SessionsController < ApplicationController
layout 'session'
def create
student = Student.authenticate(params[:student_number], params[:password])
if student.active
session[:student_id] = student.id
redirect_to student_main_path, :notice => 'Welcome!'
elsif (student and student.student_status == 3) or (student and !student.active)
flash.now.alert = "You can't login because #REASON_I_AM_TRYING_TO_CALL"
render 'new'
else
....
end
end
I would like to show students their inactivation reason on the systems if they can't login.
How can I call my INACTIVATION_REASONS from this controller file? Is it possible?
Thanks in advance!
That's just a constant, so you can call it as constant anywhere.
StudentInactivationLog::INACTIVATION_REASONS
Update
I realized actually what you want is to use a reason code or short name saved in db to represent the string.
If so, I recommend you to use the short name directly as Hash. "id" looks redundant for this light case.
INACTIVATION_REASONS = {"HTY"=>"You didn't study enough!",
"KS"=>"Graduated!",
"SBK"=>"Other Reason"}
validates :inactivation_reason, inclusion: { in: INACTIVATION_REASONS.keys,
message: "%{value} is not a valid short name" }
def full_reason_message
INACTIVATION_REASONS[self.inactivation_reason]
end
Then, to show full message of a reason in controller
reason = #student.full_reason_message
This is the idea. I havn't checked your other model codes. You'll need to save reason as the short name instead of id, and need to revise/remove some code if you decide to use it in this way.