Update stock before saving template with nested_attributes and Gem Cocoon

Asked

Viewed 170 times

0

Friends, I have a form that uses nested_attributes and I use Gem Cocoon. I have 3 models involved. Variation Containing Stock, Order, Containing Order and Parent of Detail Containing Order Detail.

The rescue is working properly, however I need to update each item’s stock before saving.

Campos:

Details:

t.string   "order_id"
t.string   "cod_produto"
t.string   "desc_produto"
t.string   "cod_cor"
t.string   "desc_cor"
t.string   "desc_tamanho"
t.decimal  "preco"
t.integer  "quantidade"
t.datetime "created_at"
t.datetime "updated_at"
t.float    "total"

Orders:

t.string   "customer_id"
t.decimal  "valor_total"
t.integer  "item_total"
t.string   "order_num"
t.datetime "created_at"
t.datetime "updated_at"

Variations:

create_table "variations", force: true do |t|
  t.string   "product_id"
  t.string   "size_id"
  t.string   "color_id"
  t.integer  "quantity"
  t.string   "barcode"
  t.datetime "created_at"
  t.datetime "updated_at"
do

The models are:

order.Rb

class Order < ActiveRecord::Base
  has_many :details, dependent: :destroy
  belongs_to :customer

  accepts_nested_attributes_for :details, reject_if: proc { |attributes| attributes['cod_produto'].blank? }, :allow_destroy => true

  validates :customer_id, :presence => true
  after_validation :sum_details
  after_create :set_order_num

  private

  def set_order_num
    update(order_num: "#{Date.current.year}-#{self.id}")
  end

  def sum_details
    total = 0
    qtd = 0
    self.details.each do |d|
      total += (d.quantidade * d.preco) if d.quantidade.present?
      qtd += d.quantidade if d.quantidade.present?
    end
    self.valor_total = total
    self.item_total = qtd
  end
end

Detail.Rb

class Detail < ActiveRecord::Base
  belongs_to :order

  after_validation :update_quantity

  before_save :get_quantity

  private

  def get_quantity
    @qtd = self.quantidade
  end

  def update_quantity
    if self.quantidade > 0
      self.quantidade = self.quantidade - @qtd
    end
  end
end

What I need to do is: before saving the Detail model (remembering that it is a child form of the order model), I need to find the amount of a product that is in the Variations model and update its quantity. To do this you have to take the Detail model the cod_product + cod_color + desc_size and look for the Barcode field in the variation model to update your stock. I’m using before_save to try to catch the amount but even this isn’t working... Could someone give some light ?

Thank you

EDITED: Inclusion of controllers:

orders_controller.Rb

class OrdersController < ApplicationController
before_action :set_order, only: [:show, :edit, :update, :destroy]
require "json"
def consulta_produto
  @variacao = Variation.find_by(barcode:params[:barcode])
  if @variacao.present?
     hash = {:cod_produto => @variacao.product.cod ,:desc_produto => @variacao.product.descricao,:cod_cor => @variacao.color.cod, :desc_cor => @variacao.color.descricao,:desc_tamanho => @variacao.size.descricao,:preco => @variacao.product.price }
     render :json => { :resultado => hash }
  else
    render :json => {:resultado => "error"}
  end
end
# GET /orders
# GET /orders.json
def index
  @orders = Order.all
end
# GET /orders/1
# GET /orders/1.json
def show
end
# GET /orders/new
def new
  @order = Order.new
end
# GET /orders/1/edit
def edit
end
# POST /orders
# POST /orders.json
def create
  @order = Order.new(order_params)
  respond_to do |format|
  if @order.save
    format.html { redirect_to @order, notice: 'Order was successfully created.' }
    format.json { render :show, status: :created, location: @order }
  else
    format.html { render :new }
    format.json { render json: @order.errors, status: :unprocessable_entity }
  end
end
end
# PATCH/PUT /orders/1
# PATCH/PUT /orders/1.json
def update
respond_to do |format|
  if @order.update(order_params)
    format.html { redirect_to @order, notice: 'Order was successfully updated.' }
    format.json { render :show, status: :ok, location: @order }
  else
    format.html { render :edit }
    format.json { render json: @order.errors, status: :unprocessable_entity }
end
end
end
# DELETE /orders/1
# DELETE /orders/1.json
def destroy
@order.destroy
respond_to do |format|
  format.html { redirect_to orders_url, notice: 'Order was successfully destroyed.' }
  format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_order
  @order = Order.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def order_params
  params.require(:order).permit(:customer_id, :valor_total, :item_total, :order_num, details_attributes: [:id,:order_id, :cod_produto, :desc_produto, :cod_cor, :desc_cor, :desc_tamanho,:preco,:quantidade,:total, :_destroy])
end
end

details_controller.Rb

before_action :set_detail, only: [:show, :edit, :update, :destroy]

# GET /details
# GET /details.json
def index
@details = Detail.all
end

# GET /details/1
# GET /details/1.json
def show
end

# GET /details/new
def new
@detail = Detail.new
end

# GET /details/1/edit
def edit
end

# POST /details
# POST /details.json
def create
@detail = Detail.new(detail_params)

respond_to do |format|
  if @detail.save
    format.html { redirect_to @detail, notice: 'Detail was successfully created.' }
    format.json { render :show, status: :created, location: @detail }
  else
    format.html { render :new }
    format.json { render json: @detail.errors, status: :unprocessable_entity }
  end
end
end

# PATCH/PUT /details/1
# PATCH/PUT /details/1.json
def update
respond_to do |format|
  if @detail.update(detail_params)
    format.html { redirect_to @detail, notice: 'Detail was successfully updated.' }
    format.json { render :show, status: :ok, location: @detail }
  else
    format.html { render :edit }
    format.json { render json: @detail.errors, status: :unprocessable_entity }
  end
end
end

# DELETE /details/1
# DELETE /details/1.json
def destroy
@detail.destroy
respond_to do |format|
  format.html { redirect_to details_url, notice: 'Detail was successfully destroyed.' }
  format.json { head :no_content }
end
end

private
# Use callbacks to share common setup or constraints between actions.
def set_detail
  @detail = Detail.find(params[:id])
end

# Never trust parameters from the scary internet, only allow the white list through.
def detail_params
  params.require(:detail).permit(:order_id, :cod_produto, :desc_produto, :cod_cor, :desc_cor, :desc_tamanho, :preco, :quantidade)
end

variations_controller.Rb

class VariationsController < ApplicationController
before_action :set_variation, only: [:show, :edit, :update, :destroy]
# require 'barby/outputter/cairo_outputter'
# def barcode_output( order )
#       barcode_string = order.barcode
#       barcode = Barby::Code128B.new(barcode_string)

#       # PNG OUTPUT
#       data = barcode.to_image(height: 15, margin: 5).to_data_url
#     end
# GET /variations
# GET /variations.json
def export
@data = Variation.order(:created_at)
# @data = Variation.select(:barcode).order(created_at: :desc)
  respond_to do |format|
    format.html { redirect_to root_url }
    format.csv { send_data @data.to_csv }
  end

end
def index

# @barcode = Barby::Code128B.new("160P10") rescue nil
# @outputter = Barby::HtmlOutputter.new(@barcode)
# @blob = Barby::PngOutputter.new(@barcode).to_png #Raw PNG data

# @barcode = Barby::HtmlOutputter.new('160P10')
# @barcode_for_html = Barby::HtmlOutputter.new(@barcode)
@variations = Variation.all
# @import = Variation::Import.new
respond_to do |format|
  format.html
  format.csv { send_data @variations}
end
# def import
#   @import = Variation::Import.new variation_import_params
#   if @import.save
#     redirect_to variations_path, notice: "Importados #{@import.imported_count} Cod Barras"
#   else
#     @variations = Variation.all
#     flash[:alert] = "Foram encontrados #{@import.errors.count} erros no CSV"
#     render action: index
#   end


# end
# require 'barby/outputter/png_outputter'
# blob = Barby::PngOutputter.new("12345").to_png #Raw PNG data
# File.open('barcode.png', 'wb'){|f| f.write blob }
#Convenience method
# File.open('barcode2.png', 'wb'){|f| f.write barcode.to_png }
# @barcode = Barby::EAN13.new('000000000')
# @barcode_for_html = Barby::CairoOutputter.new("0123456789")

# Variation.new.barcode       # => Barby::Code39 object
# Variation.new.barcode_data  # => <Barby::Code39 object>.to_png
end

# GET /variations/1
# GET /variations/1.json
def show
end

# GET /variations/new
def new
@variation = Variation.new
# @variacoes_js = Variation.joins(:color, :product, :size).includes(:color, :product, :size)
# # ActiveRecord::Base.include_root_in_json = true
# gon.variacoes = @variacoes_js.to_json
# @tamanhos = Size.order(:descricao).select(:id,:descricao)
# gon.tamanhos = @tamanhos.to_json
# @produtos = Product.order(:cod).select(:cod,:descricao)
# gon.produtos = @produtos.to_json

end

# GET /variations/1/edit
def edit
end

# POST /variations
# POST /variations.json
def create
@variation = Variation.new(variation_params)

respond_to do |format|
  if @variation.save
    format.html { redirect_to variations_path, notice: 'Variation was successfully created.' }
    format.json { render :index, status: :created, location: @variation }
  else
    format.html { render :new }
    format.json { render json: @variation.errors, status: :unprocessable_entity }
  end
end
end

# PATCH/PUT /variations/1
# PATCH/PUT /variations/1.json
def update
respond_to do |format|
  if @variation.update(variation_params)
    format.html { redirect_to @variation, notice: 'Variation was successfully updated.' }
    format.json { render :show, status: :ok, location: @variation }
  else
    format.html { render :edit }
    format.json { render json: @variation.errors, status: :unprocessable_entity }
  end
end
end

# DELETE /variations/1
# DELETE /variations/1.json
def destroy
@variation.destroy
respond_to do |format|
  format.html { redirect_to variations_url, notice: 'Variation was successfully destroyed.' }
  format.json { head :no_content }
end
end

private
# Use callbacks to share common setup or constraints between actions.
def set_variation
  @variation = Variation.find(params[:id])
end

# Never trust parameters from the scary internet, only allow the white list through.
def variation_params
  params.require(:variation).permit(:product_id, :size_id, :color_id, :quantity, :barcode)
end
def variation_import_params
  params.require(:variation_import).permit(:file)

end
end
  • vc can add more information to facilitate help, for example: 1) what is the result of executing your code? (everything is 0?); 2) how the parameters are coming to your controller?

  • Include the controllers in the question. As for the result it is simply not entering before_save.I put a debug point there but it does not reach. The way I thought about doing but do not know how it is to take the amount of each Detail, search the variation and update the variation with that new quantity, adding or subtracting. if the amount of Detail > 0 then I decrease in the stock (variation) if <0 I sum (because there was devolucao). How would that be ?

  • a hint is: if you are not entering before_save then some is wrong in the controller and the object is not being saved. Put a Debugger in the create from Ordercontroller and see if the parameters are arriving correctly

  • Yes the parameters are ok in the create’s Debugger. I placed a Debugger in before_save and on the console I tried to recover the amount, but from the error: (rdb:1) self.quantity *** Nametory Exception: Undefined method `quantity' for Activesupport::Callbacks::Filters::Before:Class (rdb:1) self Activesupport::Callbacks::Filters::Before

1 answer

1

I advise you to create a service that does this, otherwise your model will be flooded with business rules and will not guarantee reliability in transactions.

For such cases where an action will involve more than 1 record in the database it is strongly recommended to create transactions, which will ensure that the two operations take place or no.

Do a little research on PORO, service, decoupled business rules of the model, it may require an extra effort at first, but it will save you a lot of headache in the future.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.