I have a rails app which deals with product prices from different countries.
Storing GBP, and USD is working fine. However, when I start to deal with the CLP (Chilian peso) which has 3 decimal places, it's not storing right.
For example, I am trying to insert 799.990 but when it goes in its storing as 799.99. It is not ideal as there could be a price of 799.99.
I have the price set to t.decimal :price, precision: 19, scale: 4 so this should cover most currencies.
So, my question is there any way to store the price with the trailing 0? Or am I missing something, is there a better way to deal with currencies in rails?
Update:
After searching a bit more on StackOverflow, I think this may be an easier way of dealing with currencies.
number_to_currency locale converting
As 799.99 is same as 799.990 in terms of the amount, you don't need to modify the database column to store the trailing "0". You can simply edit your view to display 3 digits after decimal:
<% if #product.currency == 'CLP' %>
Price: <%= '%.3f' % #product.price %>
<% else %>
<!-- Existing logic -->
<% end %>
Update
Another option is to use sprintf:
<%= sprintf('%.3f', #product.price) %>
As Jagdeep already wrote in his answer, you only need to format the number when you render it to a page.
I would use Rails' number_to_currency helper for this:
CURRENCY_OPTIONS = {
'CLP' => { unit: '$', precision: 3 },
'EUR' => { unit: '€', precision: 2 },
'USD' => { unit: '$', precision: 2 }
}
number_to_currency(price, CURRENCY_OPTIONS[currency])
Related
I spent most part of today trying to figure out how to give my integer fields a currency format and was bombarded with a myriad of different approaches from using Money, Rails-Money gems to using custom helper methods or locales.
You can achieve this by using the number_to_currency method. Say you
have the following view:
view.html.erb
<ul>
<li><%= client.payment %></li>
</ul>
Assuming the column payments is of type integer, this will print out just that, a number with no formatting. Ex: 5523
Now add the number_to_currency method and specify which currency unit
you'd like to use (can be any)
<ul>
<li><%= number_to_currency(client.payment, :unit => "$") %></li>
</ul>
Now we get something like this: 5.523.00$
The number_to_currency method helper has some options by default, one
of those is that it inverts (as opposed to common practice) the usage
of comma and period. These can be modified by adding the options
:separator and :delimiter and give them the values you'd like as is
below.
<ul>
<li><%= number_to_currency(client.payment, :unit => "$", :separator => ".", :delimiter => ",") %></li>
</ul>
These are the available options for the number_to_currency method helper (RubyOnRailsAPI):
:locale - Sets the locale to be used for formatting (defaults to current locale).
:precision - Sets the level of precision (defaults to 2).
:unit - Sets the denomination of the currency (defaults to “$”).
:separator - Sets the separator between the units (defaults to “.”).
:delimiter - Sets the thousands delimiter (defaults to “,”).
:format - Sets the format for non-negative numbers (defaults to “%u%n”). Fields are %u for the currency, and %n for the number.
:negative_format - Sets the format for negative numbers (defaults to prepending a hyphen to the formatted number given by :format). Accepts the same fields than :format, except %n is here the absolute value of the number.
:raise - If true, raises InvalidNumberError when the argument is invalid.
You can do it a few different ways. Personally I would recommend doing this in a decorator (https://github.com/drapergem/draper) to remove some logic and formatting out of the view.
As mentioned in a prior answer you can use number_to_currency and not use other options to get the correct formatting you are looking for. It can take an integer, float, or string.
number_to_currency 10000
=> "$10,000.00"
Another way is to use money-rails (https://github.com/RubyMoney/money-rails) if you want to deal with a Money object:
money = Money.new(10000)
=> #<Money fractional:10000 currency:USD>
humanized_money_with_symbol money
=> "$10,000.00"
Have you tried using it without any options?
number_to_currency(client.payment)
It should default to $ and has worked fine for me without any options.
I'm a beginner to ruby and ruby on rails, and I'm attempting to create a complex form using simple_form. Everything is working already, but I wanted to customize the "time" field in a very specific way.
When I use:
<%= f.input :hour %>
It renders two select fields, being the "hour" field populated with options from 00 to 23, and the "minute" field populated with options from 00 to 59.
But that's not what I want. I want to replace the use of two select fields for the time with a single select field with custom pre-populated options, with time ranging from 8am until 22pm, in 15 minutes increments, and also have the text for the options show times with the syntax "8h00", "8h15", "8h30", "8h45", "9h00", "18h00", "18h15", "18h30", "18h45", etc.. being "21h45" the last option available.
I have managed to customize it a bit more using these options:
<%= f.input :hour, :as => :time, :minute_step => 15 %>
Although that solves the 15 minute increment problem, it still renders two select fields.
This would be the html equivalent of what I wanted to have simple_form render for me:
<select name="hour">
<option value="08:00:00">08h00</option>
<option value="08:15:00">08h15</option>
<option value="08:30:00">08h30</option>
<option value="08:45:00">08h45</option>
...
<option value="21:30:00">21h30</option>
<option value="21:45:00">21h45</option>
</select>
I'm pretty sure this is very easy to implement using a collection that loops from 8..21 and then loops again with '00', '15', '30' and '45', and then outputs options in the syntax that I want (hour + "h" + minute).
I want to create a custom "date" field, because there will many date fields in a single form (for a "appointment" related application that I'm creating), but since I'm a beginner on ruby on rails, I'm really lost on what would be the most smart way to implement this. I'm not sure if I should implement a custom field in simple_form, if I should use a helper function, or what.
With help from Carlos Antonio da Silva, from simpleform's mailing list, we've fixed this by creating a custom input like this:
app/inputs/hour_input.rb
class HourInput < SimpleForm::Inputs::Base
def input
#builder.select(attribute_name, hour_options, { :selected => selected_value }, { :class => "input-medium" })
end
private
# The "Selecione..." string could also be translated with something like: I18n.t("helpers.select.prompt')
def hour_options
hour = [['Selecione...', '00:00:00']]
(8..21).each do |h|
%w(00 15 30 45).each do |m|
hour.push ["#{h}h#{m}", "#{"%02d" % h}:#{m}:00"]
end
end
hour
end
def selected_value
value = object.send(attribute_name)
value && value.strftime("%H:%M:%S")
end
end
and then <%= f.input :hora, :as => :hour %> in the view.
With suggestions from simpleform's google mailing list, this is how I fixed my own problem:
in the view file, I created a input like this:
<%= f.input :hour, collection: options_for_time_select, selected: f.object.hour.strftime("%H:%M:%S") %>
And in application_helper.rb file I created a function this way:
module ApplicationHelper
def options_for_time_select
hour = Array.new
for $h in 8..21 do
for $m in ['00', '15', '30', '45'] do
hour.push [$h.to_s + "h" + $m.to_s, "%02d" % $h + ":" + $m + ":00"]
end
end
hour
end
end
I have no idea if this is the most elegant way to solve this problem, or if it really works on every scenario. I would love to get corrections or a better solution, if possible.
I have text fields for taking amount like 12345.67890. Instead of showing all 5 digits after dot (67890) I would like to show 67 only.
To do this in the text_field if I mention :precision => 2 then it is working perfectly.
But if I have 100 text fields I don't want to place every where. I'm looking for a DRY method. What advice do you have?
DRY it up, extract to a helper method!
# helper class
class SomeHelper
def text_field_precision_2 f, name
f.text_field name, :precision => 2
end
end
# view
<%= text_field_precision_2 f, :price %>
I installed the online shopping framework Spree on top of Rails 3.1.3 and Ruby 1.9.3. I also use the Spree_i18n gem to localize the shop. Now, whenever I save a product, the price is multiplied by 100.
For example, in the admin area, I type the price 3.20. That results in the value 320. If I save again, it changes to 32000 and so on.
Here is my localized de_numbers.yml for reference:
---
de:
number:
currency:
format:
format: "%u%n"
unit: "€"
precision: 2
separator: '.'
delimiter: ','
I can not think of anything unusual in my setup so I wonder why this is not a common problem. Any help will be appreciated.
EDIT:
spree-core: the Product form is not handling displaying product.price and product.cost_price with regards to I18n / localization. To fix this to work for you, you'll need to modify the core. I'm going to post to the Spree Core team about this, but in the interim, I've tested this fix and it should work.
In /gems/spree_core-1.0.0/app/views/spree/admin/products/_form.html.erb, you will need to modify these lines:
<%= f.text_field :price, :value => number_with_precision(#product.price, :precision => 2) %>
to be this:
<%= f.text_field :price, :value => number_with_precision(#product.price, :precision => I18n.t('number.currency.format.precision'), :separator => I18n.t('number.currency.format.separator'), :delimiter => I18n.t('number.currency.format.delimiter')) %>
and this:
<%= f.text_field :cost_price, :value => number_with_precision(#product.cost_price, :precision => 2) %>
to be this:
<%= f.text_field :cost_price, :value => number_with_precision(#product.cost_price, :precision => I18n.t('number.currency.format.precision'), :separator => I18n.t('number.currency.format.separator'), :delimiter => I18n.t('number.currency.format.delimiter')) %>
Essentially, we're making it handle I18n potential values.
ORIGINAL:
I've duplicated your file exactly, and tried a few tests to recreate this (create a new product, new product variant, change the product price, cost price, etc.). To re-create this, you need to create a de_numbers.yml, and flip your localization to be 'de' in the Spree initializer with "config.default_locale = 'de'"
Here are some suggested fixes:
make sure you run bundle install
in your Gemfile, make sure you're using the latest version of i18n (
gem 'spree_i18n', :git => 'git://github.com/spree/spree_i18n.git')
fix your whitespace to be 2 spaces, instead of tabs (this is potentially a white-space issue where it can't read your i18n values)
Go into the rails console, and log out the values (i.e.
I18n.t('number.currency.format.unit')
Try getting this to work in "en" locale first and then "de".
Put your values into the "de.yml" or "en.yml" first and see if they work before putting into a "de_currency.yml" file.
I'm guessing you've transposed the meanings of your separator and delimiter characters. The setup looks correct, so my thinking is that the price needs to be entered as
3,20
Rather than
3.20
This discussion of currency formatting, although not specific to Ruby development, may provide additional insight.
What I'm trying to do:
I'm trying to find out if there is a quick way to get country_select to store integers representing the countries instead of storing the country names/strings themselves. I'm using the country select gem.
Possible solution:
One way I'm thinking this can be done is if I install the gem as a plugin and edit the countries array to have arrays within the main array with integers and a string e.g. COUNTRIES = [["United Kingdom", 1],["United States", 2]]
This way in my form there will be values representing the strings. Then I can have a country_id column in my profiles table where users selected countries id will be stored. I will have a separate table "countries" that will have the countries stored in it and I'll use the country_id of the profiles table to reference the correct country in the countries table.
Doing it this way would mean I would still get the good features of the gem/plugin such as having priority countries at the top of the select list. Something I don't know how to do on my own.
It would take long but it could work. If I chose this solution where would I put the plugin? vendors folder in my apps directory right?
But if there is a quicker way to do this I'd like to do it that way.
A bigger picture:
Ok I have a search form where a user can browse the sites users filtering out results by:
text typed location
gender
sexuality
marital status
country
I'm using thinking sphinx and when filtering attributes it seems that the attributes need to be represented integers because everything works but the country.
I'm using the country select gem and it seems to only store strings and not an integer representing the string.
I would like to have it store integers instead.
Here are some contants I use in my search forms:
module ApplicationHelper
GENDER = [["Select", nil],["Male", 1],["Female", 2]]
ETHNICITY = [["Select", nil],['Black', 1 ],['White / Caucasian', 2 ],['European', 3 ],['Asian', 4 ],
['Indian', 5 ],['Middle Eastern', 6 ],['Native American', 7 ],['Hispanic', 8 ],
['Mixed Race', 9 ],['Other Ethnicity', 10 ]]
MARITAL_STATUS = [[' Select', nil],['Single', 1 ],['Dating', 2 ],['In relationship', 3 ],['Married', 4 ],
['Living Together', 5 ],['Divorced', 6 ],['Separated', 7 ],['Widowed', 8 ]]
SEXUAL_PREFERENCE = [[' Select', nil],['Straight', 1 ],['Gay', 2 ],['Bi-sexual', 3 ]]
end
The search/browse form:
<%= form_tag browsers_path, :method => 'get' do %>
<p>
Location: <%= text_field_tag :location, params[:location] %>
<br />
Country: <%= country_select :country, [ "United Kingdom", "France", "Germany" ] %>
<br />
Gender: <%= select_tag :gender, options_for_select(ApplicationHelper::GENDER, params[:gender]) %>
<br />
Ethnicity: <%= select_tag :ethnicity, options_for_select(ApplicationHelper::ETHNICITY, params[:ethnicity]) %>
<br />
Marital status: <%= select_tag :marital_status, options_for_select(ApplicationHelper::MARITAL_STATUS, params[:marital_status]) %>
<br />
Sexual preference: <%= select_tag :sexual_preference, options_for_select(ApplicationHelper::SEXUAL_PREFERENCE, params[:sexual_preference]) %>
<br />
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
as you can see each array has a string and an integer. If you check out the array from the country_select gem there are just strings.
I'd appreciate an explanation of the best possible way to do what I'm trying to do and a clear example if possible.
I hope this post made sense.
Kind regards
I ended up recreating the country list from wikipedia with name of country and iso code as value. Much more straight forward and I got to store countries as their iso code and as integers making it possible to use the attribute with thinking sphinx.
Create the constant like you have for the other filters. Then use a virtual attribute to translate and set the values so that you can store the int version and not the string.
EDIT:
Maybe I do not understand what we are talking about exactly but if you are trying to store the value in a model somewhere you can do something like this:
def country_int(country_s)
country = COUNTRY[country_s]
end
def country_int
COUNTRY.key(country)
end