Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 441
- Log:
Fix bug raised in Ticket #285.
- Author:
- rool
- Date:
- Fri Aug 30 06:16:44 +0100 2013
- Size:
- 10759 Bytes
1 | ######################################################################## |
2 | # File:: currencies_helper.rb |
3 | # (C):: Hipposoft 2009, 2010, 2011 |
4 | # |
5 | # Purpose:: Utility methods for currency-related views. |
6 | # ---------------------------------------------------------------------- |
7 | # 08-Mar-2009 (ADH): Created. |
8 | # 18-Feb-2011 (ADH): Imported from Artisan. |
9 | ######################################################################## |
10 | |
11 | module CurrenciesHelper |
12 | |
13 | # As currencyhelp_print below, but composes an integer part and a fraction |
14 | # part, expressed as strings, into one price. Non-numeric characters in |
15 | # either part are stripped (though, in addition to the currency symbol, |
16 | # non-numeric characters might appear in the output because they're present |
17 | # in the formatting template for the given currency). |
18 | # |
19 | # The final optional parameter is passed through to currencyhelp_print - |
20 | # see there for details. |
21 | # |
22 | # See also: currencyhelp_print() |
23 | # |
24 | def currencyhelp_compose( currency, integer, fraction, converter = false ) |
25 | return currencyhelp_print( |
26 | currency, |
27 | Currency.simplify( integer, fraction ), |
28 | converter |
29 | ) |
30 | end |
31 | |
32 | # Use the given currency's settings to print a fully formatted monetary |
33 | # value passed in as a string containing a floating point value, or a type |
34 | # which can convert to a string with "to_s" and give a floating point style |
35 | # result (e.g. a BigDecimal or an actual Float). |
36 | # |
37 | # If the optional "converter" parameter is set to 'true', then HTML which |
38 | # pops up a currency conversion hint via Google will be included, but only |
39 | # if there's a currently logged in user (so the target currency is known) |
40 | # and only if the currency requested differs from the target. |
41 | # |
42 | # See also: currencyhelp_compose() |
43 | # |
44 | def currencyhelp_print( currency, value, converter = false ) |
45 | rounding = Currency::ROUNDING_ALGORITHMS[ currency.rounding_algorithm ] || |
46 | Currency::ROUNDING_ALGORITHMS[ Currency::DEFAULT_ROUNDING_ALGORITHM ] |
47 | numeric = rounding[ :proc ].call( currency, value.to_s ) |
48 | decimal = numeric.index( '.' ) |
49 | |
50 | # Split up the number, which is stored as a string in mathematical |
51 | # "integer-dot-fraction" notation. |
52 | |
53 | if ( decimal.nil? ) |
54 | fraction = '' |
55 | integer = numeric |
56 | else |
57 | fraction = numeric[ ( decimal + 1 )..-1 ] |
58 | integer = numeric[ 0..( decimal - 1 ) ] |
59 | end |
60 | |
61 | # Use the template formatter to process these. |
62 | |
63 | formatted_integer = currencyhelp_format( |
64 | ( currency.integer_template || '' ).reverse, |
65 | integer |
66 | ) |
67 | |
68 | formatted_fraction = currencyhelp_format( |
69 | ( currency.fraction_template || '' ), |
70 | fraction |
71 | ) |
72 | |
73 | formatted_value = formatted_integer |
74 | formatted_value += currency.delimiter + formatted_fraction unless ( formatted_fraction.empty? ) |
75 | |
76 | # Canvass doesn't use Locations like Artisan. |
77 | # |
78 | # # Build a currency converter link? |
79 | # |
80 | # if ( converter && |
81 | # logged_in? && |
82 | # ! current_user.location.nil? && |
83 | # ! current_user.location.currency.nil? && |
84 | # current_user.location.currency_id != currency.id ) |
85 | # |
86 | # converter_link = " " + link_to( |
87 | # image_tag( |
88 | # 'famfamfam_silk_icons/currency.png', |
89 | # :size => '16x16', |
90 | # :alt => '£', |
91 | # :align => 'top', |
92 | # :class => 'currency_converter' |
93 | # ), |
94 | # "https://www.google.com/search?q=convert+#{ formatted_value }+#{ currency.code }+to+#{ current_user.location.currency.code }", |
95 | # :target => '_blank', |
96 | # :class => 'help' |
97 | # ) |
98 | # |
99 | # end # (Else converter_link ends up 'nil' by default) |
100 | converter_link = nil |
101 | |
102 | # Compile the result |
103 | |
104 | if ( currency.show_after_number ) |
105 | return "#{ formatted_value }#{ currency.symbol }#{ converter_link }" |
106 | else |
107 | return "#{ currency.symbol }#{ formatted_value }#{ converter_link }" |
108 | end |
109 | end |
110 | |
111 | # For a given user or currency object, return form fields for the given form |
112 | # object (that is, the "f" in view code such as "form for <foo> do |f|...") |
113 | # which let someone input a price for a given named field in either the given |
114 | # currency or a currency appropriate to the given user's configured location. |
115 | # |
116 | # The optional fourth parameter is used if the text input field generator |
117 | # methods are not going to be able to determine the initial value (if any) |
118 | # to use, perhaps because the fields are being generated for a non- |
119 | # ActiveRecord object, such as a simple hash. Provide this object (e.g. a |
120 | # hash) in the fourth parameter; the field names used ("<name>_integer" and |
121 | # "<name>_fraction") are looked up in this object with the "[]" operator to |
122 | # obtain the value. |
123 | # |
124 | # Text fields are generated for the given name with "_integer" or "_fraction" |
125 | # appended, for the integer and fractional parts respectively. |
126 | # |
127 | def currencyhelp_edit( user_or_currency, form, name, data_source = nil ) |
128 | currency = ( user_or_currency.is_a?( User ) ) ? Currency.get_best_currency( user_or_currency ) : user_or_currency |
129 | result = '' |
130 | |
131 | # TODO: This is for the variable "column names" (attribute names) in the |
132 | # currency editor row(s) for advanced artwork search originally. The search |
133 | # model's "price" column is a serialized hash of the price data. The form |
134 | # calling to this method uses proper nested attributes format, but even if |
135 | # the hash is converted to an OpenStruct, the contents are ignored (noting |
136 | # that "form.object" is always "nil" herein). The solution of passing in a |
137 | # parameter as presently implemented is a nasty hack just to avoid wasting |
138 | # a lot of time on a cleaner mechanism - hence "to do" later, if ever! |
139 | |
140 | extra = {} |
141 | field = "#{ name }_integer" |
142 | value = data_source[ field ] unless ( data_source.nil? ) |
143 | extra[ :value ] = value || "0" |
144 | |
145 | result << currency.symbol unless ( currency.show_after_number ) |
146 | result << form.text_field( |
147 | field.to_sym, |
148 | { |
149 | :size => 5, # 5 digits - that's a costly item! |
150 | :maxlength => Currency::MAXLEN_INTEGER_TEMPLATE |
151 | }.merge( extra ) |
152 | ) |
153 | |
154 | if ( currency.decimal_precision > 0 ) |
155 | extra = {} |
156 | field = "#{ name }_fraction" |
157 | value = data_source[ field ] unless ( data_source.nil? ) |
158 | extra[ :value ] = value || "0" * ( currency.fraction_template.length ) |
159 | |
160 | result << ( currency.delimiter || '' ) |
161 | result << form.text_field( |
162 | field.to_sym, |
163 | { |
164 | :size => currency.decimal_precision, |
165 | :maxlength => currency.decimal_precision |
166 | }.merge( extra ) |
167 | ) |
168 | end |
169 | |
170 | result << currency.symbol if ( currency.show_after_number ) |
171 | return result |
172 | end |
173 | |
174 | # Given an internal rounding algorithm name, return a human-readable |
175 | # equivalent including hint string if available. Returned value is *not* |
176 | # escaped with "h". |
177 | # |
178 | def currencyhelp_rounding_algorithm( algorithm ) |
179 | details = Currency::ROUNDING_ALGORITHMS[ algorithm ] |
180 | humanized = algorithm.humanize |
181 | |
182 | if ( details.nil? ) |
183 | humanized |
184 | else |
185 | "#{ humanized } (#{ details[ :hint ] })" |
186 | end |
187 | end |
188 | |
189 | # Return the result of 'select_tag' for a list of all currencies (this may be |
190 | # modified by internal sorting, to avoid creating unnecessary copies). Runs |
191 | # via apphelp_menu in the back-end so see that for details; broadly, pass the |
192 | # array of Currency objects you want to list and an options hash which will |
193 | # contain ":form" for a form_for based menu or nothing/":selected" for a |
194 | # stand-alone menu (in which case, "params[ :currency_id ]" will be set when |
195 | # the surrounding form is submitted). |
196 | # |
197 | def currencyhelp_menu( currencies, options = {} ) |
198 | Currency.apply_default_sort_order( currencies ) |
199 | values = currencies.collect { | c | [ "#{ h c.name }", c.id ] } |
200 | |
201 | return apphelp_menu( values, :currency_id, options ) |
202 | end |
203 | |
204 | # As "currencyhelp_menu" but builds a rounding algorithm selector menu |
205 | # instead of a currency selector menu. Sets the "rounding_algorithm" field |
206 | # of a form_for <some object> / sets params[ :rounding_algorithm ] for |
207 | # stand-alone menus. |
208 | # |
209 | def currencyhelp_rounding_algorithm_menu( options = {} ) |
210 | algorithms = Currency::ROUNDING_ALGORITHMS |
211 | values = Currency::ROUNDING_ALGORITHM_ORDER.collect do | name | |
212 | [ currencyhelp_rounding_algorithm( name ), name ] |
213 | end |
214 | |
215 | return apphelp_menu( values, :rounding_algorithm, options ) |
216 | end |
217 | |
218 | # As "currencyhelp_menu" but provides a fixed preset list of decimal |
219 | # precision choices. |
220 | # |
221 | def currencyhelp_decimal_precision_menu( options = {} ) |
222 | values = [] |
223 | precisions = { |
224 | '4' => '4: 1234.1234', |
225 | '3' => '3: 1234.123', |
226 | '2' => '2: 1234.12', |
227 | '1' => '1: 1234.1', |
228 | '0' => '0: 1234', |
229 | '-1' => '-1: 1230', |
230 | '-2' => '-2: 1200', |
231 | '-3' => '-3: 1000' |
232 | } |
233 | |
234 | sorted = precisions.keys.sort { | a, b | b.to_i <=> a.to_i } |
235 | values = sorted.collect do | key | |
236 | [ precisions[ key ], key.to_i ] |
237 | end |
238 | |
239 | return apphelp_menu( values, :decimal_precision, options ) |
240 | end |
241 | |
242 | private |
243 | |
244 | # Run through a template strings from left to right adding characters from |
245 | # a given integer value (expressed as a string) if there's a numeric entry in |
246 | # the template, else adding the template's non-numeric character at that |
247 | # position. If there's anything left of the value string once the loop has |
248 | # finished, add the remaining data to the front of the formatted result. |
249 | # |
250 | # Using per-byte processing means that multibyte characters drop out "in |
251 | # the wash", since they'll all be added from the template before the next |
252 | # numeric character (0-9 => single byte) gets encountered - bearing in mind |
253 | # that this is a Rails application using UTF-8 encoding in Ruby via $KCODE. |
254 | # |
255 | # Can be used to process integer parts of floats-as-strings by passing in the |
256 | # template as "template.reverse", or for the fraction parts by passing in the |
257 | # template directly. See currencyhelp_print for example usage. |
258 | # |
259 | def currencyhelp_format( template, value ) |
260 | formatted = '' |
261 | |
262 | template.each_byte do | byte | |
263 | break if ( value.empty? ) |
264 | |
265 | byte = '' << byte # Convert from character code to string |
266 | add = ( byte < '0' || byte > '9' ) ? byte : value.slice!( -1..-1 ) |
267 | formatted = "#{ add }#{ formatted }" |
268 | end |
269 | |
270 | formatted = "#{ value }#{ formatted }" unless ( value.empty? ) |
271 | return formatted |
272 | end |
273 | end |