sale.rb "Parent"
class Sale < ActiveRecord::Base
has_many :branch_history_solds
accepts_nested_attributes_for :branch_history_solds, :reject_if => lambda { |a| a[:content].blank? },
:allow_destroy => true
end
class SalesController < ApplicationController
before_action :set_sale, only: [:show, :edit, :update, :destroy]
# GET /sales
# GET /sales.json
def index
@sales = Sale.all
end
# GET /sales/1
# GET /sales/1.json
def show
end
# GET /sales/new
def new
@sale = Sale.new
@sale.branch_history_solds.build
end
# GET /sales/1/edit
def edit
end
# POST /sales
# POST /sales.json
def create
@sale = Sale.create(sale_params)
# @sale.branch_history_solds.build
respond_to do |format|
if @sale.save
format.html { redirect_to @sale, notice: 'Sale was successfully created.' }
format.json { render :show, status: :created, location: @sale }
else
format.html { render :new }
format.json { render json: @sale.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /sales/1
# PATCH/PUT /sales/1.json
def update
@sale = Sale.find(params[:id])
respond_to do |format|
if @sale.update_attributes(sale_params)
format.html { redirect_to @sale, notice: 'Sale was successfully updated.' }
format.json { render :show, status: :ok, location: @sale }
else
format.html { render :edit }
format.json { render json: @sale.errors, status: :unprocessable_entity }
end
end
end
# DELETE /sales/1
# DELETE /sales/1.json
def destroy
@sale.destroy
respond_to do |format|
format.html { redirect_to sales_url, notice: 'Sale was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_sale
@sale = Sale.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def sale_params
params.require(:sale).permit(:receipt_no, :customer_name, :phone_number, :email, :branch_id, :paid, branch_history_sold_attributes: [:id, :sold, :branch_product_id])
end
end
branch_history_sold.rb "Child"
class BranchHistorySold < ActiveRecord::Base
belongs_to :sale
end
class BranchHistorySoldsController < ApplicationController
def index
@search = BranchHistorySold.ransack(params[:q])
@branch_sold_histories = @search.result(distinct: true).group(:name).sum(:sold)
end
def create
@sale = Sale.find(params[:sale_id]) # Find specific branch_product we will be working with
@branch_history_sold = @sale.branch_history_solds.create(branch_history_sold_params) # Enable whitelisted attributes to get created
flash[:notice] = "New products have been Sold from branch" # flash notice will show immediately after branch_history_sold gets created
redirect_to branch_product_path(@branch_product) # redirect to branch_product show page
end
def destroy
@sale = Sale.find(params[:sale_id]) # Find specific branch_product we will be working with
@branch_history_sold = @sale.branch_history_solds.find(params[:id]) # Find specific branch_history_sold that will be destroyed
@branch_history_sold.destroy # destroy branch_history_sold
flash[:notice] = "Newly sold products have been added back to branch" # flash notice will show immediately after branch_history_sold is destroyed
redirect_to branch_product_path(@branch_product) # redirect to branch_product show page
end
private
def branch_history_sold_params
params.require(:branch_history_sold).permit(:sold, :customer_name) # whitelisted attributes
end
end
And finally my form with the fields_for attribute
<div class="container">
<div class="row">
<%= form_for @sale, html: { class: "form-horizontal" } do |f| %>
<!-- Text input-->
<div class="form-group">
<label class="col-md-1 control-label">R/NO</label>
<div class="col-md-6">
<%= f.collection_select(:branch_id, Branch.all, :id, :name) %>
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-1 control-label">R/NO</label>
<div class="col-md-6">
<%= f.text_field :receipt_no, placeholder: "Receipt number", class: "form-control input-md" %>
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-1 control-label" >Name</label>
<div class="col-md-6">
<%= f.text_field :customer_name, placeholder: "Prince Abalogu", class: "form-control input-md" %>
</div>
</div>
<!-- Appended Input-->
<div class="form-group">
<label class="col-md-1 control-label">Number</label>
<div class="col-md-6">
<%= f.text_field :phone_number, placeholder: "08185438075", class: "form-control input-md" %>
</div>
</div>
<!-- Appended Input-->
<div class="form-group">
<label class="col-md-1 control-label">E-mail</label>
<div class="col-md-6">
<%= f.text_field :email, placeholder: "[email protected]", class: "form-control input-md" %>
</div>
</div>
<!-- Appended Input-->
<%= f.fields_for :branch_history_solds, @sale.branch_history_solds.build do |b| %>
<div class="form-group">
<label class="col-md-1 control-label">Product</label>
<div class="col-md-6">
<%= b.number_field :sold, placeholder: "Quantity" %>
</div>
</div>
<div class="form-group">
<label class="col-md-1 control-label"></label>
<div class="col-md-6">
<% @branch = BranchProduct.where :branch_id, 19 %>
<%= b.collection_select(:branch_product_id, BranchProduct.where(branch_id: params[:branch_id] ), :id, :name ) %> Select Product
</div>
</div>
<% end %>
<!-- Appended Input-->
<div class="form-group">
<label class="col-md-1 control-label"></label>
<div class="col-md-6">
<%= f.check_box :paid %> Paid
</div>
</div>
<!-- Button (Double) -->
<div class="form-group">
<label class="col-md-1 control-label"></label>
<div class="col-md-8">
<%= f.button :submit %>
</div>
</div>
<% end %>
</div>
</div>
The form displays but the only problem im having now is that it doesnt create any branch_history_solds after I submit
The problem is that the lambda you are using to evaluate if the nested records should be rejected will always return true since your form/model does not have a content
attribute:
class Sale < ActiveRecord::Base
has_many :branch_history_solds
accepts_nested_attributes_for :branch_history_solds, :reject_if => lambda { |a| a[:content].blank? },
:allow_destroy => true
end
You're also whitelisting the wrong attributes branch_history_sold
not branch_history_solds
.
You need to change this to be an attribute that is actually passed:
class Sale < ActiveRecord::Base
has_many :branch_history_solds
accepts_nested_attributes_for :branch_history_solds, :reject_if => lambda { |a| a[:branch_product_id].blank? },
:allow_destroy => true
end
However your general setup is just plain strange and I don't know if its just the naming but it does not make much sense.
If you want to create a point of sales system or order management system you would do it like so:
class Order
belongs_to :customer
has_many :line_items
has_many :products, through: :line_items
accepts_nested_attributes_for :line_items,
allow_destroy: true,
reject_if: -> { |li| li[:product_id].blank? || li[:quantity].blank? }
end
# columns:
# - order_id [integer, index, foreign key]
# - product_id [integer, index, foreign key]
# - quantity [decimal or integer]
# - price [decimal]
# - subtotal [decimal]
class LineItem
belongs_to :order
belongs_to :product
end
class Product
has_many :line_items
has_many :orders, through: :line_items
end
class OrdersController
def new
end
def create
@order = Order.new(order_params) do
order.customer = current_user
end
if (@order.save)
else
end
end
private
def order_params
params.require(:order)
.permit(:foo, :bar, line_item_attributes: [:product_id, :quantity])
end
end
Note that you should only let users pass an extremely limited number of params - never take things like prices from the user. The actual pricing logic should be done on the model layer. You also wan't to detach the customer details from the order model - otherwise every repeat order will duplicate data. I would spend some time getting the actual domain model down before adding additional features like search.