Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to restrict current_user from adding more than 3 order_items to a order per time period?

I'm building a store in Rails that has a specific sales model. I need to allow a user to add only 3 items to his order per 30 days. The 30 days counter should start upon adding the first order_item. Once 30 days expires, user would be able to add 3 orders. If 30 days didn't pass and for an example, user adds two order_items he would still be allowed to add one more order_item within 30 days. So as well if user tries to add more then 3 items to show an error message and disregard saving of the order_items to current_user's order.

I have products, orders, order_items, users. I guess that I should add something to user model but I'm not sure what.

order_items_controller.rb

def create
    @order = current_order
    @order_item = @order.order_items.new(order_item_params)
    @order.user_id = current_user.id
    @order.save
    session[:order_id] = @order.id

  respond_to do |format|
    format.js { flash[:notice] = "ORDER HAS BEEN CREATED." } 
  end
  end
private
  def order_item_params
    params.require(:order_item).permit(:quantity, :product_id, :user_id)
  end
end

user.rb

class User < ActiveRecord::Base
  has_many :identities, dependent: :destroy
  has_many :order
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :omniauthable, :invitable, :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable
end

order_item.rb

class OrderItem < ActiveRecord::Base
  belongs_to :product
  belongs_to :order

  validates :quantity, presence: true, numericality: { only_integer: true, greater_than: 0 }
  validate :product_present
  validate :order_present

  before_save :finalize

  def unit_price
    if persisted?
      self[:unit_price]
    else
      product.price
    end
  end

  def total_price
    unit_price * quantity
  end

private
  def product_present
    if product.nil?
      errors.add(:product, "is not valid or is not active.")
    end
  end

  def order_present
    if order.nil?
      errors.add(:order, "is not a valid order.")
    end
  end

  def finalize
    self[:unit_price] = unit_price
    self[:total_price] = quantity * self[:unit_price]
  end
end

order.rb

class Order < ActiveRecord::Base
  belongs_to :order_status
  has_many :order_items
  before_create :set_order_status
  before_save :update_subtotal

  def subtotal
    order_items.collect { |oi| oi.valid? ? (oi.quantity * oi.unit_price) : 0 }.sum
  end
private
  def set_order_status
    self.order_status_id = 1
  end

  def update_subtotal
    self[:subtotal] = subtotal
  end
end

carts_controller.rb

class CartsController < ApplicationController
  def show
    @order_items = current_order.order_items
  end

routes.rb

resources :order_items, only: [:create, :update, :destroy, :new]

form.html.erb

<%= form_for OrderItem.new,  html: {class: "add-to-cart"}, remote: true do |f| %>


        <div class="input-group">
          <%= f.hidden_field :quantity, value: 1, min: 1 %>
          <div class="input-group-btn">
            <%= f.hidden_field :product_id, value: product.id %>
            <%= f.submit "Add to Cart", data: { confirm: 'Are you sure that you want to order this item for current month?'}, class: "btn btn-default black-background white" %>
          </div>
        </div>
      <% end %>
    </div>
like image 302
Marko I. Avatar asked Oct 18 '22 06:10

Marko I.


1 Answers

I would add a begin_date and a order_counter to user model. Every time you add an order, look if the begin_date is more than 30 days ago, then set the begin_date to the actual date. If the begin_date is less than 30 days ago, increase the counter. And if the counter ist already 3 refuse the order.

You can add the columns to the user table by the command line argument

rails generate migration AddOrderCounterToUser

This will create a class in db/migrations:

class AddPartNumberToProducts < ActiveRecord::Migration
  def change
    add_column :users, :begin_date, :date
    add_column :users, :order_counter, :integer 
  end
end

Add the additional attributes in your UserController to permit them in user_params.

Then change the create method in your OrderItemController

def create
  now = Date.today
  success = false
  if current_user.begin_date && ((now - 30) < current_user.begin_date)
     if current_user.order_counter >= 3
        # deal with the case that order should not be created, 
        # for example redirect.
     else
       current_user.order_counter += 1
       current_user.save
       success = true
     end
  else 
    current_user.order_counter = 1
    current_user.begin_date = now
    current_user.save
    success = true
  end
  if success
    @order = current_order
    @order_item = @order.order_items.new(order_item_params)
    @order.user_id = current_user.id
    @order.save
    session[:order_id] = @order.id

    respond_to do |format|
      format.js { flash[:notice] = "ORDER HAS BEEN CREATED." } 
    end
  else 
    respond_to do |format|
      format.js { flash[:notice] = "CREATION NOT POSSIBLE." } 
    end
  end
end

You can also put the checking code in a method in the user model, that would be cleaner.

like image 140
irene Avatar answered Oct 20 '22 23:10

irene