Project

General

Profile

Feature #22024 » 22024-v3.patch

Go MAEDA, 2024-01-01 11:26

View differences:

app/helpers/application_helper.rb
266 266
    when Integer
267 267
      object.to_s
268 268
    when Float
269
      sprintf "%.2f", object
269
      number_with_delimiter(sprintf('%.2f', object), delimiter: nil)
270 270
    when User, Group
271 271
      html ? link_to_principal(object) : object.to_s
272 272
    when Project
......
684 684

  
685 685
  def html_hours(text)
686 686
    text.gsub(
687
      %r{(\d+)([\.:])(\d+)},
687
      %r{(\d+)([\.,:])(\d+)},
688 688
      '<span class="hours hours-int">\1</span><span class="hours hours-dec">\2\3</span>'
689 689
    ).html_safe
690 690
  end
lib/redmine/export/pdf/issues_pdf_helper.rb
21 21
  module Export
22 22
    module PDF
23 23
      module IssuesPdfHelper
24
        include ActionView::Helpers::NumberHelper
25

  
24 26
        # Returns a PDF string of a single issue
25 27
        def issue_to_pdf(issue, assoc={})
26 28
          pdf = ITCPDF.new(current_language)
......
406 408
                elsif value.is_a?(Time)
407 409
                  format_time(value)
408 410
                elsif value.is_a?(Float)
409
                  sprintf "%.2f", value
411
                  number_with_delimiter(sprintf('%.2f', value), delimiter: nil)
410 412
                else
411 413
                  value
412 414
                end
lib/redmine/field_format.rb
539 539
      end
540 540

  
541 541
      def validate_single_value(custom_field, value, customized=nil)
542
        value = normalize_float(value)
542 543
        errs = super
543
        errs << ::I18n.t('activerecord.errors.messages.invalid') unless (Kernel.Float(value) rescue nil)
544
        errs << ::I18n.t('activerecord.errors.messages.invalid') unless Kernel.Float(value, exception: false)
544 545
        errs
545 546
      end
546 547

  
lib/redmine/i18n.rb
21 21

  
22 22
module Redmine
23 23
  module I18n
24
    include ActionView::Helpers::NumberHelper
25

  
24 26
    def self.included(base)
25 27
      base.extend Redmine::I18n
26 28
    end
......
95 97
        m = ((hours - h) * 60).round
96 98
        "%d:%02d" % [h, m]
97 99
      else
98
        "%.2f" % hours.to_f
100
        number_with_delimiter(sprintf('%.2f', hours.to_f), delimiter: nil)
99 101
      end
100 102
    end
101 103

  
104
    # Will consider language specific separator in user input
105
    # and normalize them to a unified format to be accepted by Kernel.Float().
106
    #
107
    # @param value [String] A string represenation of a float value.
108
    #
109
    # @note The delimiter cannot be used here if it is a decimal point since it
110
    #       will clash with the dot separator.
111
    def normalize_float(value)
112
      separator = ::I18n.t('number.format.separator')
113
      value.gsub(/[#{separator}]/, separator => '.')
114
    end
115

  
102 116
    def day_name(day)
103 117
      ::I18n.t('date.day_names')[day % 7]
104 118
    end
test/helpers/application_helper_test.rb
2171 2171
    end
2172 2172
  end
2173 2173

  
2174
  def test_format_hours_should_use_locale_decimal_separator
2175
    to_test = {'en' => '0.75', 'de' => '0,75'}
2176
    with_settings :timespan_format => 'decimal' do
2177
      to_test.each do |locale, expected|
2178
        with_locale locale do
2179
          assert_equal expected, format_hours(0.75)
2180
        end
2181
      end
2182
    end
2183
  end
2184

  
2174 2185
  def test_html_hours
2175 2186
    assert_equal '<span class="hours hours-int">0</span><span class="hours hours-dec">:45</span>',
2176 2187
                 html_hours('0:45')
test/unit/lib/redmine/export/pdf/issues_pdf_test.rb
33 33
    time_entry = TimeEntry.create!(:spent_on => Date.today, :hours => 4.3432, :user => user, :author => user,
34 34
                     :project_id => 1, :issue => issue, :activity => TimeEntryActivity.first)
35 35

  
36
    results = fetch_row_values(issue, query, 0)
37
    assert_equal ["2", "Add ingredients categories", "4.34"], results
36
    to_test = {'en' => '4.34', 'de' => '4,34'}
37
    to_test.each do |locale, expected|
38
      with_locale locale do
39
        results = fetch_row_values(issue, query, 0)
40
        assert_equal ['2', 'Add ingredients categories', expected], results
41
      end
42
    end
38 43
  end
39 44

  
40 45
  def test_fetch_row_values_should_be_able_to_handle_parent_issue_subject
test/unit/lib/redmine/field_format/numeric_format_test.rb
23 23
class Redmine::NumericFieldFormatTest < ActionView::TestCase
24 24
  include ApplicationHelper
25 25

  
26
  fixtures :projects, :users, :issue_statuses, :enumerations,
27
           :trackers, :projects_trackers, :roles, :member_roles,
28
           :members, :enabled_modules
29

  
26 30
  def setup
27 31
    User.current = nil
28 32
  end
......
34 38
    assert_equal 3, field.format.formatted_custom_value(self, custom_value, false)
35 39
    assert_equal '<a href="http://foo/3" class="external">3</a>', field.format.formatted_custom_value(self, custom_value, true)
36 40
  end
41

  
42
  def test_float_field_value_should_validate_when_given_with_various_separator
43
    field = IssueCustomField.generate!(field_format: 'float')
44
    issue = Issue.generate!(tracker: Tracker.find(1), status: IssueStatus.find(1), priority: IssuePriority.find(6))
45
    to_test = {'en' => '3.33', 'de' => '3,33'}
46
    to_test.each do |locale, expected|
47
      with_locale locale do
48
        assert field.format.validate_single_value(field, expected, issue)
49
      end
50
    end
51
  end
52

  
53
  def test_float_field_should_format_with_various_locale_separator
54
    field = IssueCustomField.generate!(field_format: 'float')
55
    issue = Issue.generate!(tracker: Tracker.find(1), status: IssueStatus.find(1), priority: IssuePriority.find(6))
56
    issue.custom_field_values = { field.id => '1234.56' }
57
    issue.save!
58
    to_test = {'en' => '1234.56', 'de' => '1234,56'}
59
    to_test.each do |locale, expected|
60
      with_locale locale do
61
        assert_equal expected, format_object(issue.reload.custom_field_values.last, false)
62
      end
63
    end
64
  end
37 65
end
(3-3/3)