Regards! I am creating an inventory system in which, at the time of making the sale, I need to write the code or product name, I need to get the price out of it, without the need to write it manually, however, I have little knowledge of javascript, I have found several resources like autocomplete of jquery, however, I can not do it, I followed the tutorial of RailsCasts about dynamic selectors, nevertheless I need this to be achieved with text fields, I appreciate immensely someone can help me, specifically in the output model, I have the items in the output_item model, and my product model:
output_form.html.erb
<%= form_with(model: output, local: true, id: "form") do |f| %>
<div class="form-group">
<%= f.label :invoice %> <br>
<%= f.text_field :invoice, class: "form-control", placeholder: "CE0001" %>
</div>
<div class="form-group">
<%= f.label :customer %> <br>
<%= f.text_field :customer, class: "form-control", placeholder: "Ej. Jeovanny" %>
</div>
<table width="100%" id="output-item">
<thead>
<th>Product</th>
<th>Quantity</th>
<th>Price</th>
<th>Total</th>
<th>Options</th>
</thead>
<tbody>
<%= f.fields_for :output_items, id: "form" do |item| %>
<%= render "output_item_fields", f: item %>
<% end %>
</tbody>
</table>
<div class="form-group mb-0">
<%= link_to_add_association 'Add product', f, :output_items, :"data-association-insertion-node" => "table#output-item",:"data-association-insertion-method" => "append", class: "btn btn-success" %>
</div>
<div class="form-group>
<%= link_to "Cancelar", outputs_path, class: "btn btn-danger" %>
<%= f.submit "Send", class: "btn btn-primary" %>
</div>
<% end %>
output_items_fields.html.erb
<tr class="item">
<td><%= f.text_field :product_id, class: "form-control field" %></td>
<td><%= f.text_field :quantity, class: "form-control field quantity" %></td>
<td><%= f.text_field :price, class: "form-control field price" %></td>
<td><input type="text" class="form-control field subtotal"></td>
<td class="text-center">
<%= link_to_remove_association f, { wrapper_class: "item", class: "btn btn-danger" } do %>
<i class="fal fa-trash-alt"></i>
<% end %>
</td>
</tr>
product.rb
class Product < ApplicationRecord
has_many :input_items
has_many :output_items
validates :code, :name, presence: true
def purchase
input_items.pluck(:quantity).sum
end
def sale
output_items.pluck(:quantity).sum
end
def stock
(input_items.pluck(:quantity).sum - output_items.pluck(:quantity).sum)
end
def price
self.input_items.sum(:price) / self.input_items.count
end
end
Output.rb
class Output < ApplicationRecord
has_many :output_items, inverse_of: :output, :dependent => :delete_all
accepts_nested_attributes_for :output_items, reject_if: :all_blank, allow_destroy: true
validates :invoice, :provider, presence: true
end
output_item.rb
class OutputItem < ApplicationRecord
belongs_to :product
belongs_to :output
validates :quantity, :price, numericality: true, presence: true
end
class Product < ApplicationRecord
has_many :output_items
end
input.rb
class Input < ApplicationRecord
has_many :input_items, inverse_of: :input, :dependent => :delete_all
accepts_nested_attributes_for :input_items, reject_if: :all_blank, allow_destroy: true
validates :invoice, :provider, presence: true
end
input_item.rb
class InputItem < ApplicationRecord
belongs_to :product
belongs_to :input
validates :quantity, :price, numericality: true, presence: true
end
input_migrate.rb
class CreateInputs < ActiveRecord::Migration[5.2]
def change
create_table :inputs do |t|
t.string :invoice
t.string :provider
t.timestamps
end
end
end
input_items_migration.rb
class CreateInputItems < ActiveRecord::Migration[5.2]
def change
create_table :input_items do |t|
t.integer :product_id
t.float :quantity
t.float :price
t.belongs_to :input, foreign_key: true
t.timestamps
end
end
end
product_migrate.rb
class CreateProducts < ActiveRecord::Migration[5.2]
def change
create_table :products do |t|
t.string :code
t.string :name
t.float :utility
t.timestamps
end
end
end
output_migrate.rb
class CreateOutputs < ActiveRecord::Migration[5.2]
def change
create_table :outputs do |t|
t.string :invoice
t.string :customer
t.timestamps
end
end
end
output_item_migrate.rb
class CreateOutputItems < ActiveRecord::Migration[5.2]
def change
create_table :output_items do |t|
t.integer :product_id
t.float :quantity
t.float :price
t.belongs_to :output, foreign_key: true
t.timestamps
end
end
end
The code that RailsCast offers, to autocomplete selectors is with:
(function() {
jQuery(function() {
var states;
$('#input_price').parent().hide();
states = $('#input_price').html();
return $('#input_product_id').change(function() {
var country, escaped_country, options;
country = $('#input_product_id :selected').text();
escaped_country = country.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g, '\\$1');
options = $(states).filter("optgroup[label='" + escaped_country + "']").html();
if (options) {
$('#input_price').html(options);
return $('#input_price').parent().show();
} else {
$('#input_price').empty();
return $('#input_price').parent().hide();
}
});
});
}).call(this);
In what way could adapt, or what is the most convenient way to do this, as an added data, I use cocoon for dynamic items, I appreciate immensely, can help me, Regards!
Updates
output_items_fields.html.erb
<tr class="item">
<td><%= f.text_field :product_id, class: "form-control field" %></td>
<td><%= f.text_field :quantity, class: "form-control field quantity" %></td>
<td><%= f.text_field :price, class: "form-control field price" %></td>
<td><input type="text" class="form-control field subtotal"></td>
<td class="text-center">
<%= link_to_remove_association f, { wrapper_class: "item", class: "btn btn-danger" } do %>
<i class="fal fa-trash-alt"></i>
<% end %>
</td>
</tr>
<% content_for :javascript do %>
<script type="text/javascript">var product_info = $.parseJSON('<%= @product_info.to_json.html_safe %>');</script>
<% end %>
<% content_for :javascript do %>
<script type="text/javascript">
var product_info = $.parseJSON('<%= @product_info.to_json.html_safe %>');
(function() {
jQuery(function() {
var product = product_info[$('#output_product_id')]
return $('#output_product_id').change(function() {
if (product) {
$('#output_price').val(product.price);
$('#output_description').val(product.description);
} else {
$('#output_price').val("");
}
});
});
}).call(this);
</script>
<% end %>
outputs_controller.rb
class OutputsController < ApplicationController
before_action :set_output, only: [:show, :edit, :update, :destroy]
def new
@output = Output.new
@output.output_items.build
@product_info = Product.joins(:input_items).select(:price).all.inject({}) {|a, b| a[b.input_items] = {price: b.price}}
end
end
When I look for the product_info and testing in the browser console, I get it:
console.log(product_info)
Object { price: null }
debugger eval code:1:1
undefined
console.log('testing')
testing debugger eval code:1:1
undefined
As long as you don't have a very large number of products, this is how I would tackle this to auto populate based on model number (if I understand your requirements correctly). You essentially need to get a Javascript representation of all of your product data, then feed that to jQuery to do the populating, similar to how the Railscast was using country name. And lucky for you, you can easily convert a ruby Hash or Array in to something Javascript can see using .to_json.
First, create a hash that has a key of product model to product price, and populate it from your data source:
@product_info = Product.select(:code,:price).all.inject({}) {|a, b| a[b.code] = {price: b.price}}
And put that in your controller action.
Next, get that instance variable in to a Javascript variable on your page. This part depends on if you are set up to use content_for :javascript - see Including inline javascript using content_for in rails .
<% content_for :javascript do %>
<script type="text/javascript">
var product_info = $.parseJSON(<%= @product_info.to_json.html_safe %>);
</script>
<% end %>
The finally, add this take on the Railscast Code to the content_for block and make it look like the following (replacing the old content_for :javascript block). This part is up to you - if you want a text box that is just populated, or if you want what they had, where you hide and show the field if you can find the values in the hash. I just made it populate it if it could find it, or make it a blank string if it couldn't
<% content_for :javascript do %>
<script type="text/javascript">
var product_info = $.parseJSON(<%= @product_info.to_json.html_safe) %>;
(function() {
jQuery(function() {
return $('#input_product_id').change(function() {
if (var product = product_info[$('#input_product_id')]) {
$('#input_price').val(product.price);
} else {
$('#input_price').val("");
}
});
});
}).call(this);
</script>
<% end %>
Now this would work decently as a first iteration, but there are some issues with it that may apply to you, depending on how you are set up.
The more products you have, the more data you have to spit out as a JSON string. If that string becomes 2, 4, 10 megabytes, it will really slow your users down as they have to load that data just to view your page. You can mitigate that by going a different route and firing off an AJAX call to get the product data as the user types in the model number, and populate the fields as we're doing here. It's not that much more work to go that route as long as you're OK with having an endpoint that gets hit every time the user types something in a text field, and the corresponding database call it needs to make to do the searching.
The other issue is that every time the page loads, you will be re-fetching all of your product data. You would need to start thinking about caching that data either in the view, or in the controller, so that you always load the product model->price/description map from cache instead of fetching it from the database.
Finally, if some other user is entering product information which you want available to power your auto populate code, it would require a page reload. If you don't have that many products being added/changed, this isn't as big of a concern, but if it changes multiple times per minute/hour, you may want to think about going with the aforementioned solution which fires an AJAX call every time the user types something in, so you can be guaranteed of the latest Product information being returned, rather that some potentially stale representation of all of your product information.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.