fmt: shared number formatting for HTML and PDF
This commit is contained in:
parent
9e85082758
commit
f7319a08a9
4 changed files with 54 additions and 18 deletions
11
asxpio.rb
11
asxpio.rb
|
|
@ -17,6 +17,7 @@ $root = __dir__
|
||||||
$logger = Logger.new($stdout)
|
$logger = Logger.new($stdout)
|
||||||
$env = ENV.fetch('RACK_ENV', 'development')
|
$env = ENV.fetch('RACK_ENV', 'development')
|
||||||
|
|
||||||
|
require_relative 'lib/fmt'
|
||||||
require_relative 'lib/mailer'
|
require_relative 'lib/mailer'
|
||||||
require_relative 'lib/rate_limit'
|
require_relative 'lib/rate_limit'
|
||||||
require_relative 'lib/db'
|
require_relative 'lib/db'
|
||||||
|
|
@ -73,18 +74,12 @@ class AsxpioWeb < Sinatra::Base
|
||||||
request.ip
|
request.ip
|
||||||
end
|
end
|
||||||
|
|
||||||
# Exchange rate for display: full captured precision (up to 8 dp), trailing
|
|
||||||
# zeros trimmed, padded to at least min_dp. Mirrors InvoicePdf#fmt_rate so
|
|
||||||
# the HTML and PDF agree. (4 dp matches the NBG's GEL quoting.)
|
|
||||||
def fmt_rate(value, min_dp = 4)
|
def fmt_rate(value, min_dp = 4)
|
||||||
frac = BigDecimal(value.to_s).round(8).to_s('F').split('.').last.sub(/0+$/, '')
|
Fmt.rate(value, min_dp: min_dp)
|
||||||
whole = BigDecimal(value.to_s).to_i
|
|
||||||
"#{whole}.#{frac.ljust(min_dp, '0')}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# LTC amount: up to 8 dp, trailing zeros trimmed, no padding.
|
|
||||||
def fmt_ltc(value)
|
def fmt_ltc(value)
|
||||||
BigDecimal(value.to_s).round(8).to_s('F').sub(/(\.\d*?)0+$/, '\1').sub(/\.$/, '')
|
Fmt.ltc(value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
25
lib/fmt.rb
Normal file
25
lib/fmt.rb
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
require 'bigdecimal'
|
||||||
|
|
||||||
|
# Number formatting shared by the HTML views and the PDF so they can't drift.
|
||||||
|
module Fmt
|
||||||
|
module_function
|
||||||
|
|
||||||
|
# Exchange rate: full captured precision (up to 8 dp), trailing zeros
|
||||||
|
# trimmed, padded to at least min_dp so it reads as a rate. (4 dp matches
|
||||||
|
# the NBG's GEL quoting.)
|
||||||
|
def rate(value, min_dp: 4)
|
||||||
|
int, frac = BigDecimal(value.to_s).round(8).to_s('F').split('.')
|
||||||
|
frac = (frac || '').sub(/0+$/, '').ljust(min_dp, '0')
|
||||||
|
"#{group(int)}.#{frac}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# LTC amount: up to 8 dp, trailing zeros trimmed, no padding.
|
||||||
|
def ltc(value)
|
||||||
|
BigDecimal(value.to_s).round(8).to_s('F').sub(/(\.\d*?)0+$/, '\1').sub(/\.$/, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
# "1234567" -> "1,234,567"
|
||||||
|
def group(int)
|
||||||
|
int.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -2,6 +2,7 @@ require 'prawn'
|
||||||
require 'prawn/table'
|
require 'prawn/table'
|
||||||
require 'bigdecimal'
|
require 'bigdecimal'
|
||||||
require 'stringio'
|
require 'stringio'
|
||||||
|
require_relative 'fmt'
|
||||||
|
|
||||||
class InvoicePdf
|
class InvoicePdf
|
||||||
FONTS_DIR = File.join($root, 'public', 'fonts')
|
FONTS_DIR = File.join($root, 'public', 'fonts')
|
||||||
|
|
@ -333,18 +334,11 @@ class InvoicePdf
|
||||||
end
|
end
|
||||||
|
|
||||||
def fmt_ltc(value)
|
def fmt_ltc(value)
|
||||||
BigDecimal(value.to_s).round(8).to_s('F').sub(/(\.\d*?)0+$/, '\\1').sub(/\.$/, '')
|
Fmt.ltc(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Exchange rate: keep full captured precision (up to 8 dp), trim trailing
|
|
||||||
# zeros, but pad to at least `min_dp` so it reads as a rate. GEL/USD rates are
|
|
||||||
# quoted to 4 dp by the NBG, so the default min is 4.
|
|
||||||
def fmt_rate(value, min_dp: 4)
|
def fmt_rate(value, min_dp: 4)
|
||||||
s = BigDecimal(value.to_s).round(8).to_s('F')
|
Fmt.rate(value, min_dp: min_dp)
|
||||||
int, frac = s.split('.')
|
|
||||||
frac = (frac || '').sub(/0+$/, '')
|
|
||||||
frac = frac.ljust(min_dp, '0')
|
|
||||||
"#{with_thousands("#{int}.00").split('.').first}.#{frac}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fmt_qty(value)
|
def fmt_qty(value)
|
||||||
|
|
@ -354,7 +348,7 @@ class InvoicePdf
|
||||||
|
|
||||||
def with_thousands(numstr)
|
def with_thousands(numstr)
|
||||||
int, frac = numstr.split('.')
|
int, frac = numstr.split('.')
|
||||||
int = int.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
int = Fmt.group(int)
|
||||||
frac ? "#{int}.#{frac.ljust(2, '0')[0, 2]}" : "#{int}.00"
|
frac ? "#{int}.#{frac.ljust(2, '0')[0, 2]}" : "#{int}.00"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
22
test/fmt_test.rb
Normal file
22
test/fmt_test.rb
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
require_relative 'test_helper'
|
||||||
|
|
||||||
|
class FmtTest < Minitest::Test
|
||||||
|
def test_rate_pads_to_min_dp
|
||||||
|
assert_equal '2.9500', Fmt.rate('2.95')
|
||||||
|
assert_equal '3.0000', Fmt.rate(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_rate_keeps_captured_precision
|
||||||
|
assert_equal '2.95123456', Fmt.rate('2.95123456')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_rate_groups_thousands
|
||||||
|
assert_equal '1,050.2500', Fmt.rate('1050.25')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_ltc_trims_trailing_zeros
|
||||||
|
assert_equal '1.25', Fmt.ltc('1.250000')
|
||||||
|
assert_equal '2', Fmt.ltc('2.0')
|
||||||
|
assert_equal '0.00123456', Fmt.ltc('0.00123456')
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue