Adding Cart items Ruby on Rails - ruby-on-rails

I am developing a very basic shopping cart and the issue I am having is that if I add more than one of the same product to my shopping cart I am not seeing the quantity increase and am instead just seeing multiple versions of the item.
For example I see:
1 x green light = £15
1 x green light = £15
rather than seeing:
2 x green light = £30
How can I make it so that my shopping cart will check for multiple versions in the cart and then add them together?
Currently I have:
Application Controller
def current_cart
if session[:cart_id]
#current_cart ||= Cart.find(session[:cart_id])
end
if session[:cart_id].nil?
#current_cart = Cart.create!
session[:cart_id] = #current_cart.id
end
#current_cart
end
Cart Controller
def show
#cart = current_cart
end
Cart Model
has_many :items
def total_price
items.to_a.sum(&:full_price)
end
Cart View
<table id="line_items">
<tr>
<th>Product</th>
<th>Qty</th>
<th class="price">Unit Price</th>
<th class="price">Full Price</th>
</tr>
<% for item in #cart.items %>
<tr class="<%= cycle :odd, :even %>">
<td><%=h item.product.name %></td>
<td class="qty"><%= item.quantity %></td>
<td class="price"><%= gbp(item.unit_price) %></td>
<td class="price"><%= gbp(item.full_price) %></td>
</tr>
<% end %>
<tr>
<td class="total price" colspan="4">
Total: <%= gbp(#cart.total_price) %>
</td>
</tr>
</table>
FURTHER INFORMATION
Products#Index
<%= link_to "Add to Cart", line_items_path(:product_id => product), :method => :post %>
Any advice people can offer would be much appreciated. Thanks!
New Setup - Causing Error Uninitialised Constant CartController
Routes.rb
Checkout::Application.routes.draw do
ActiveAdmin.routes(self)
devise_for :admin_users, ActiveAdmin::Devise.config
post '/add_to_cart/:product_id' => 'cart#add_to_cart', :as => 'add_to_cart'
resources :carts
resources :products
resources :items
root :to => 'products#index'
end
Carts Controller
class CartsController < ApplicationController
def show
#cart = current_cart
end
def add_to_cart
current_cart.add_item(params[:product_id])
redirect_to carts_path(current_cart.id)
end
end
Carts Model
class Cart < ActiveRecord::Base
has_many :items
def add_item(product_id)
item = items.where('product_id = ?', product_id).first
if item
# increase the quantity of product in cart
item.quantity + 1
save
else
# product does not exist in cart
product = Product.find(product_id)
items << product
end
save
end
def total_price
items.to_a.sum(&:full_price)
end
end
Product#Index
<table class="jobs">
<thead>
<tr>
<th scope="col" id="name">Product Code</th>
<th scope="col" id="company">Name</th>
<th scope="col" id="company">Price</th>
<th scope="col" id="company">Action</th>
</tr>
</thead>
<tbody>
<% #product.each do |product| %>
<tr>
<td><%= product.product_code %></td>
<td><%= product.name %></td>
<td><%= gbp(product.price) %></td>
<td><%= button_to "Add to Cart", add_to_cart_path(:product_id => product), :method => :post %></td>
</tr>
<% end %>
</tbody>
</table>

In your Cart model, create a method called
def add_item(product_id)
item = items.where('product_id = ?', product_id).first
if item
# increase the quantity of product in cart
item.quantity + 1
save
else
# product does not exist in cart
cart.items << Item.new(product_id: product_id, quantity: 1)
end
save
end
In routes.rb,
post '/add_to_cart/:product_id' => 'cart#add_to_cart', :as => 'add_to_cart'
Change your Add to Cart route to a call add_to_cart method in the Cart controller.
def add_to_cart
current_cart.add_item(params[:product_id])
# redirect to shopping cart or whereever
end
This should give you the idea of what you are wanting to accomplish.

Related

Iterate over multiple joined tables to display items in view

I have an Order model, a LineItem model and a Product model. Below are the associations. I'm trying to figure out how to access the name attribute in the Product model. My first thought was to iterate over the LineItem table to gather the line_items where order_id == order that is used in the show view. Then, iterate over the new line_items and get each product name from the product_id attribute of the line_item. This would have to be done in a method, probably in the Order model. Is there a better way this can be done using Rails associations? My current implementation results in undefined method product for #Order:0x007f5d007305f0
order.rb
class Order < ApplicationRecord
has_many :line_items
belongs_to :user
accepts_nested_attributes_for :line_items
after_validation :set_amount
private
def set_amount
self.amount = line_items.map(&:product).map(&:cost).inject(:+)
end
end
product.rb
class Product < ApplicationRecord
has_many :line_items
end
line_item.rb
class LineItem < ApplicationRecord
belongs_to :order
belongs_to :product
end
order/show.html.erb
<h3>Customer - <%= #order.user.first_name %> <%= #order.user.last_name %></h3>
<h4>Order Date - <%= #order.created_at.to_s(:date_us) %></h4>
<%= #line_items.each do |item| %>
<table class="table">
<thead>
<tr>
<th>Product Quantity</th>
<th>Product Name</th>
<th class="text-right">Subtotal</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row"></th>
<td><%= item.order.product.name %></td>
<td class="text-right"></td>
</tr>
</tbody>
</table>
<% end %>
<div class="text-right">
<h3><%= #order.amount %></h3>
</div>
orders_controller.rb
class OrdersController < ApplicationController
before_action :authenticate_user!
def index
#orders = Order.all
end
def show
#order = Order.find(params[:id])
#line_items = LineItem.all
end
def new
#order = Order.new
#order.line_items.build
end
def create
#order = current_user.orders.build(order_params)
if #order.valid?
#order.save
redirect_to order_receipt_path(#order)
else
render :new
end
end
private
def order_params
params.require(:order).permit(line_items_attributes: [:id, :name, :product_id])
end
def products
#products ||= Product.all
end
helper_method :products
end
You receive the following error, because the Order model doesn't have project association, but the LineItem has.
You might want to filter the line items by order in the controller show method:
def show
#order = Order.find(params[:id])
#line_items = #order.line_items
end
And then iterate over them and display their project names:
<h3>Customer - <%= #order.user.first_name %> <%= #order.user.last_name %></h3>
<h4>Order Date - <%= #order.created_at.to_s(:date_us) %></h4>
<%= #line_items.each do |item| %>
<table class="table">
<thead>
<tr>
<th>Product Quantity</th>
<th>Product Name</th>
<th class="text-right">Subtotal</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row"></th>
<td><%= item.product.name %></td>
<td class="text-right"></td>
</tr>
</tbody>
</table>
<% end %>
<div class="text-right">
<h3><%= #order.amount %></h3>
</div>
Order doesn't know product. You should call item.product.name instead of item.order.product.name
Also consider eager loading products to avoiding N+1 query problem. You can do this in the OrdersController#show action with: #order.line_items.includes(:product)

Ruby On Rails: Duplicate Index User listing

I have Devise installed and have set up a UsersController. My UsersController looks like the following:
class UsersController < ApplicationController
def index
#users = User.all
end
end
My routes currently look like this:
Rails.application.routes.draw do
root 'users#home'
devise_for :admins
devise_for :users, controller: :users
scope "/admin" do
resources :users, only: [:index, :show, :new]
end
I created an index page under my users folder with the following code:
<div class = 'container' style = 'width:750px'>
<table>
<thead>
<th>User ID </th>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Phone Number</th>
</thead>
<tbody>
<%= #users.each do |user| %>
<tr>
<td><%= user.id %></td>
<td><%= user.first_name %></td>
<td><%= user.last_name %></td>
<td><%= user.email %></td>
<td><%= user.country_code %><%= user.phone_number %></td>
</tr>
<%end%>
</tbody>
</table>
</div>
Everything displays perfectly, but I keep receiving this above my table:
This is a screenshot of what I am viewing with the text I want removed highlighted.
Please show me how I can fix this. Big thanks to all that submit!
You should modify your #users.each line to not use the <%=, like so:
<% #users.each do |user| %>
...
<% end %>
In ERB, <%= %> evaluates and prints the result of the code contained within, whereas <% %> only evaluates the expression within.
Hope it helps!

Rails - Updating records using Best in Place

I'm building a marketplace app where I'm trying to use the Best in Place gem to allow sellers to add a tracking number for each of their orders.
I get a NoMethodError which I'm not able to resolve.
NoMethodError in Orders#sales
undefined method `tracking' for nil:NilClass
The error points to the best in place line below in the view page. This view page is based on the method Sales (in the controller below) where I filter for orders for that particular seller.
Here is my routes.rb with order routing. Since orders need not be edited or destroyed, I didn't create an edit or delete route.
resources :listings do
resources :orders, only: [:new, :create, :update]
collection { post :import }
end
Here is a snippet from my orders controller
class OrdersController < ApplicationController
before_action :set_order, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
before_action :check_user, only: [:edit, :update]
def sales
#orders = Order.all.where(seller: current_user).order("created_at DESC")
end
def update
#order.update_attributes(params[:order])
end
def check_user
if current_user.id != #seller && current_user.name != "admin admin"
redirect_to root_url, alert: "Sorry, you are not the seller of this listing"
end
end
Here is my view page:
<table class="table table-striped table-bordered">
<tr>
<th class="col-md-2">Image</th>
<th class="col-md-2">Item</th>
<th class="col-md-1">Price</th>
<th class="col-md-2">Customer</th>
<th class="col-md-2">Date Sold</th>
<th class="col-md-2">Shipment Tracking #</th>
<th class="col-md-1">Carrier (UPS, USPS, etc.)</th>
</tr>
<% #orders.each do |order| %>
<tr>
<td><%= image_tag order.listing.image.url(:thumb) %></td>
<td><%= order.listing.name %></td>
<td><%= number_to_currency(order.listing.price) %></td>
<td><%= order.buyer.name %></td>
<td><%= order.created_at.strftime("%B %-d, %Y") %></td>
<td><%= best_in_place #order, :tracking, :type => :input %> </td>
<td><%= best_in_place #order, :carrier, :type => :input %></td>
</tr>
<% end %>
</table>
Been stuck on this for a while. Appreciate any help on this.
I think the problem is that you are calling #order inside your .each method.
Try:
<%= best in place order, :tracking, :type => :input %>
You will need to change the next line in your view as well.
I figured it out. The problem was that since I was using best_in_place in a non-Activerecord environment (as part of a table with a list of orders), I needed to pass the order id explicitly. I found this in the best_in_place documentation https://github.com/bernat/best_in_place#non-active-record-environments
I created a custom route for the update action
put 'orderupdate' => "orders#update"
Then in my do loop in the view, I used the custom path for the route above and passed the order id to that route.
<% #orders.each do |order| %>
<tr>
<td><%= order.id %></td>
<td><%= image_tag order.listing.image.url(:thumb) %></td>
<td><%= order.listing.name %></td>
<td><%= number_to_currency(order.listing.price) %></td>
<td><%= order.buyer.name %></td>
<td><%= order.created_at.strftime("%B %-d, %Y") %></td>
<td><%= best_in_place order, :tracking, :type => :input, :url => orderupdate_path(id: order.id) %> </td>
<td><%= best_in_place order, :carrier, :type => :input, :url => orderupdate_path(id: order.id) %> </td>
</tr>
<% end %>
Here is the update method in my controller:
def update
#order = Order.find(params[:id])
respond_to do |format|
if #order.update(order_params)
format.html { redirect_to sales_url, notice: 'Order updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
Hope this helps someone!

Rails children not rendering p

I have a model called Task Order that has_many Invoices. When I click "Show" for a Task Order, it is supposed to show all of the Invoices that belong to that Task Order. However, when I click "Show" it shows ALL of the invoices, even ones that do not belong to that task order. Note that I have the same relationship and views for my Contract/Task Order, but it is working correctly so I am not sure where I went wrong.
Here is my Task Order model:
class TaskOrder < ActiveRecord::Base
belongs_to :contract
has_many :invoices, :dependent => :destroy
validates_presence_of :id
validates_uniqueness_of :id
self.primary_key = :id
end
Here is my Invoice model
class Invoice < ActiveRecord::Base
belongs_to :task_order
validates_presence_of :task_order_id
end
Here is my views/task_orders/show code
Invoices:
<table>
<thead>
<tr>
<th>Invoice Date</th>
<th>Invoice #</th>
<th>PoP</th>
<th>Amount</th>
<th>Description</th>
<th>Invoice TO</th>
<th></th>
<th></th>
</tr>
</thead>
</tr>
<tbody>
<% #invoices.each do |invoice| %>
<tr>
<td><%= invoice.invoiceDate%></td>
<td><%= invoice.invoiceNumber %></td>
<td><%= invoice.PoP %></td>
<td><%= number_to_currency(invoice.amount) %></td>
<td><%= invoice.description %></td>
<td><%= invoice.task_order_id %></td>
<td><%= link_to 'Show', invoice %></td>
<td><%= link_to 'Edit', edit_invoice_path(invoice) %></td>
<td><%= link_to 'Destroy', invoice, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
And here is my Task Order controller:
def show
#invoices = Invoice.all
end
def index
#task_orders = TaskOrder.all
end
In your show method, you just want to get only the task order's invoices, rather than all invoices, which you are currently getting.
def show
#task_order = TaskOrder.find(params[:id])
#invoices = #task_order.invoices
end
This way, you don't need to change your show.html.erb file. In addition, you should take a look at the Rails Guides, which should aid you in your quest to learn Rails.

pass variables between two controllers

I have two controllers: tasks, tasksperson.
I have views/tasks/index.html.erb:
<table>
<% #tasks.group_by(&:name).each do |name, tasks| %>
<tr>
<td><%= name %></td>
<td><%= tasks.size %></td>
<td><%= tasks.select{ |task| task.done != true }.size %></td>
</tr>
<% end %>
</table>
I want to create a link in views/tasks/index.html to views/tasksperson/index.html.erb.I want also to send the name into 'index' in Tasksperson_controller.. I tried to do this by getting params[:name] but I think it's wrong
maybe, I need to do something like:
<td><%= link_to 'Show Tasks', tasksperson_path(name) %></td>
this is my tasksperson_controller:
class TaskspersonController < ApplicationController
def index
#tasks = Task.where(:name => params[:name]) respond_to do |format|
format.html # index.html.erb
format.json { render json: #tasks }
end
end
end
and views/tasksperson/index.html.erb:
<table>
<tr>
<th>Name</th>
<th>num of tasks</th>
<th>num tasks left</th>
<th>test</th>
</tr>
<% #tasks.each do |f| %>
<tr>
<td><%= f.name %></td>
<td><%= f.task %></td>
<td><%= f.done %></td>
</tr>
<% end %>
</table>
You need to add :name as a parameter to the rule that defines the route to TaskspersonController#index in routes.rb
so it would be something like this:
match 'tasksperson/index/:name' => 'tasksperson#index', as: :tasksperson_path
Based on your comment "...so Task have many taskpersons" I think you want a data model similar to below
class Task < ActiveRecord::Base
has_many :assigned_tasks
has_many :people, :through => :assigned_tasks
end
# maybe this is just the User class?
class Person < ActiveRecord::Base
has_many :assigned_tasks
has_many :tasks, :through => :assigned_tasks
end
# was TaskPerson
class AssignedTask < ActiveRecord::Base
belongs_to :task
belongs_to :person
end
See http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association for information about "The has_many :through Association"
task = Task.create(:title => "Go up the hill")
jack = Person.find(00000)
jill = Person.find(00000)
task.people << jack
task.people << jill
task.assigned_tasks.each do |join|
puts join.created_at
puts join.person.name
# 0 - jack
# 1 - jill
end
task.people.each do |person|
puts person.name
end
I am not exactly sure what you are trying to display in your views, it looks like you are grouping by a task name attribute in task/index, is that the Persons name?

Resources