diff --git a/app/services/usps/flirt_engine.rb b/app/services/usps/flirt_engine.rb index 376340d0..8c015d5b 100644 --- a/app/services/usps/flirt_engine.rb +++ b/app/services/usps/flirt_engine.rb @@ -2,129 +2,129 @@ module USPS # First-Class Letter Inverse Rating Toolkit class FLIRTEngine class << self - # this will have to be updated when they come out with a new notice 123! + # Updated for USPS Notice 123 - 2024 rate changes FCMI_RATE_TABLE = { letter: { 1.0 => { - ca: 1.65, - mx: 1.65, - other: 1.65 + ca: 1.75, + mx: 1.75, + other: 1.75 }, 2.0 => { - ca: 1.65, - mx: 2.50, - other: 2.98 + ca: 1.75, + mx: 2.65, + other: 3.16 }, 3.0 => { - ca: 2.36, - mx: 3.30, - other: 4.36 + ca: 2.50, + mx: 3.50, + other: 4.62 }, 3.5 => { - ca: 3.02, - mx: 4.14, - other: 5.75 + ca: 3.20, + mx: 4.39, + other: 6.09 } }, flat: { 1.0 => { - ca: 3.15, - mx: 3.15, - other: 3.15 + ca: 3.34, + mx: 3.34, + other: 3.34 }, 2.0 => { - ca: 3.55, - mx: 4.22, - other: 4.48 + ca: 3.76, + mx: 4.47, + other: 4.75 }, 3.0 => { - ca: 3.86, - mx: 5.16, - other: 5.78 + ca: 4.09, + mx: 5.47, + other: 6.13 }, 4.0 => { - ca: 4.12, - mx: 6.13, - other: 7.11 + ca: 4.37, + mx: 6.50, + other: 7.54 }, 5.0 => { - ca: 4.43, - mx: 7.09, - other: 8.41 + ca: 4.70, + mx: 7.52, + other: 8.92 }, 6.0 => { - ca: 4.73, - mx: 8.03, - other: 9.71 + ca: 5.02, + mx: 8.51, + other: 10.30 }, 7.0 => { - ca: 5.02, - mx: 9.01, - other: 11.01 + ca: 5.32, + mx: 9.55, + other: 11.67 }, 8.0 => { - ca: 5.32, - mx: 9.96, - other: 12.31 + ca: 5.64, + mx: 10.56, + other: 13.05 }, 12.0 => { - ca: 6.79, - mx: 12.03, - other: 14.92 + ca: 7.20, + mx: 12.75, + other: 15.82 }, 15.994 => { - ca: 8.27, - mx: 14.10, - other: 17.53 + ca: 8.77, + mx: 14.95, + other: 18.59 } } } - FCMI_NON_MACHINABLE_SURCHARGE = 0.46 + FCMI_NON_MACHINABLE_SURCHARGE = 0.48 US_LETTER_RATES = { - 1.0 => 0.69, - 2.0 => 0.97, - 3.0 => 1.25, - 3.5 => 1.53 + 1.0 => 0.73, + 2.0 => 1.02, + 3.0 => 1.31, + 3.5 => 1.60 } US_FLAT_RATES = { - 1.0 => 1.50, - 2.0 => 1.77, - 3.0 => 2.04, - 4.0 => 2.31, - 5.0 => 2.59, - 6.0 => 2.87, - 7.0 => 3.15, - 8.0 => 3.43, - 9.0 => 3.71, - 10.0 => 4.01, - 11.0 => 4.31, - 12.0 => 4.61, - 13.0 => 4.91 + 1.0 => 1.55, + 2.0 => 1.83, + 3.0 => 2.11, + 4.0 => 2.39, + 5.0 => 2.68, + 6.0 => 2.97, + 7.0 => 3.26, + 8.0 => 3.55, + 9.0 => 3.84, + 10.0 => 4.15, + 11.0 => 4.46, + 12.0 => 4.77, + 13.0 => 5.08 } US_STAMP_LETTER_RATES = { - 1.0 => 0.73, - 2.0 => 1.01, - 3.0 => 1.29, - 3.5 => 1.57 + 1.0 => 0.78, + 2.0 => 1.08, + 3.0 => 1.38, + 3.5 => 1.68 } US_STAMP_FLAT_RATES = { - 1.0 => 1.50, - 2.0 => 1.77, - 3.0 => 2.04, - 4.0 => 2.31, - 5.0 => 2.59, - 6.0 => 2.87, - 7.0 => 3.15, - 8.0 => 3.43, - 9.0 => 3.71, - 10.0 => 4.01, - 11.0 => 4.31, - 12.0 => 4.61, - 13.0 => 4.91 + 1.0 => 1.55, + 2.0 => 1.83, + 3.0 => 2.11, + 4.0 => 2.39, + 5.0 => 2.68, + 6.0 => 2.97, + 7.0 => 3.26, + 8.0 => 3.55, + 9.0 => 3.84, + 10.0 => 4.15, + 11.0 => 4.46, + 12.0 => 4.77, + 13.0 => 5.08 } # calculate the retail FCMI price for a :letter or a :flat going to a given country diff --git a/app/services/usps/pricing_engine.rb b/app/services/usps/pricing_engine.rb index b5763e0f..335ca0bc 100644 --- a/app/services/usps/pricing_engine.rb +++ b/app/services/usps/pricing_engine.rb @@ -1,129 +1,129 @@ module USPS class PricingEngine class << self - # this will have to be updated when they come out with a new notice 123! + # Updated for USPS Notice 123 - 2024 rate changes FCMI_RATE_TABLE = { letter: { 1.0 => { - ca: 1.65, - mx: 1.65, - other: 1.65 + ca: 1.75, + mx: 1.75, + other: 1.75 }, 2.0 => { - ca: 1.65, - mx: 2.50, - other: 2.98 + ca: 1.75, + mx: 2.65, + other: 3.16 }, 3.0 => { - ca: 2.36, - mx: 3.30, - other: 4.36 + ca: 2.50, + mx: 3.50, + other: 4.62 }, 3.5 => { - ca: 3.02, - mx: 4.14, - other: 5.75 + ca: 3.20, + mx: 4.39, + other: 6.09 } }, flat: { 1.0 => { - ca: 3.15, - mx: 3.15, - other: 3.15 + ca: 3.34, + mx: 3.34, + other: 3.34 }, 2.0 => { - ca: 3.55, - mx: 4.22, - other: 4.48 + ca: 3.76, + mx: 4.47, + other: 4.75 }, 3.0 => { - ca: 3.86, - mx: 5.16, - other: 5.78 + ca: 4.09, + mx: 5.47, + other: 6.13 }, 4.0 => { - ca: 4.12, - mx: 6.13, - other: 7.11 + ca: 4.37, + mx: 6.50, + other: 7.54 }, 5.0 => { - ca: 4.43, - mx: 7.09, - other: 8.41 + ca: 4.70, + mx: 7.52, + other: 8.92 }, 6.0 => { - ca: 4.73, - mx: 8.03, - other: 9.71 + ca: 5.02, + mx: 8.51, + other: 10.30 }, 7.0 => { - ca: 5.02, - mx: 9.01, - other: 11.01 + ca: 5.32, + mx: 9.55, + other: 11.67 }, 8.0 => { - ca: 5.32, - mx: 9.96, - other: 12.31 + ca: 5.64, + mx: 10.56, + other: 13.05 }, 12.0 => { - ca: 6.79, - mx: 12.03, - other: 14.92 + ca: 7.20, + mx: 12.75, + other: 15.82 }, 15.994 => { - ca: 8.27, - mx: 14.10, - other: 17.53 + ca: 8.77, + mx: 14.95, + other: 18.59 } } } - FCMI_NON_MACHINABLE_SURCHARGE = 0.46 + FCMI_NON_MACHINABLE_SURCHARGE = 0.48 US_LETTER_RATES = { - 1.0 => 0.69, - 2.0 => 0.97, - 3.0 => 1.25, - 3.5 => 1.53 + 1.0 => 0.73, + 2.0 => 1.02, + 3.0 => 1.31, + 3.5 => 1.60 } US_FLAT_RATES = { - 1.0 => 1.50, - 2.0 => 1.77, - 3.0 => 2.04, - 4.0 => 2.31, - 5.0 => 2.59, - 6.0 => 2.87, - 7.0 => 3.15, - 8.0 => 3.43, - 9.0 => 3.71, - 10.0 => 4.01, - 11.0 => 4.31, - 12.0 => 4.61, - 13.0 => 4.91 + 1.0 => 1.55, + 2.0 => 1.83, + 3.0 => 2.11, + 4.0 => 2.39, + 5.0 => 2.68, + 6.0 => 2.97, + 7.0 => 3.26, + 8.0 => 3.55, + 9.0 => 3.84, + 10.0 => 4.15, + 11.0 => 4.46, + 12.0 => 4.77, + 13.0 => 5.08 } US_STAMP_LETTER_RATES = { - 1.0 => 0.73, - 2.0 => 1.01, - 3.0 => 1.29, - 3.5 => 1.57 + 1.0 => 0.78, + 2.0 => 1.08, + 3.0 => 1.38, + 3.5 => 1.68 } US_STAMP_FLAT_RATES = { - 1.0 => 1.50, - 2.0 => 1.77, - 3.0 => 2.04, - 4.0 => 2.31, - 5.0 => 2.59, - 6.0 => 2.87, - 7.0 => 3.15, - 8.0 => 3.43, - 9.0 => 3.71, - 10.0 => 4.01, - 11.0 => 4.31, - 12.0 => 4.61, - 13.0 => 4.91 + 1.0 => 1.55, + 2.0 => 1.83, + 3.0 => 2.11, + 4.0 => 2.39, + 5.0 => 2.68, + 6.0 => 2.97, + 7.0 => 3.26, + 8.0 => 3.55, + 9.0 => 3.84, + 10.0 => 4.15, + 11.0 => 4.46, + 12.0 => 4.77, + 13.0 => 5.08 } def metered_price(processing_category, weight, non_machinable = false) diff --git a/test/services/usps/flirt_engine_test.rb b/test/services/usps/flirt_engine_test.rb new file mode 100644 index 00000000..af455d48 --- /dev/null +++ b/test/services/usps/flirt_engine_test.rb @@ -0,0 +1,48 @@ +require "test_helper" + +class USPS::FLIRTEngineTest < ActiveSupport::TestCase + test "metered letter rates match pricing engine" do + assert_equal 0.73, USPS::FLIRTEngine.metered_price(:letter, 1.0) + assert_equal 1.02, USPS::FLIRTEngine.metered_price(:letter, 2.0) + assert_equal 1.31, USPS::FLIRTEngine.metered_price(:letter, 3.0) + assert_equal 1.60, USPS::FLIRTEngine.metered_price(:letter, 3.5) + end + + test "stamp letter rates for 2024" do + assert_equal 0.78, USPS::FLIRTEngine.stamp_price(:letter, 1.0) + assert_equal 1.08, USPS::FLIRTEngine.stamp_price(:letter, 2.0) + assert_equal 1.38, USPS::FLIRTEngine.stamp_price(:letter, 3.0) + assert_equal 1.68, USPS::FLIRTEngine.stamp_price(:letter, 3.5) + end + + test "desired price for international mail" do + assert_equal 1.75, USPS::FLIRTEngine.desired_price(:letter, 1.0, "CA") + assert_equal 1.75, USPS::FLIRTEngine.desired_price(:letter, 1.0, "MX") + assert_equal 1.75, USPS::FLIRTEngine.desired_price(:letter, 1.0, "GB") + end + + test "closest us price calculation" do + # Test finding the closest US rate for international rate + result = USPS::FLIRTEngine.closest_us_price(1.75) + + assert result[:processing_category] == :letter || result[:processing_category] == :flat + assert result[:price] >= 1.75 + assert result[:difference] >= 0 + end + + test "engines have consistent rates" do + # Test that both engines return the same rates + assert_equal USPS::PricingEngine.metered_price(:letter, 1.0), + USPS::FLIRTEngine.metered_price(:letter, 1.0) + + assert_equal USPS::PricingEngine.metered_price(:flat, 1.0), + USPS::FLIRTEngine.metered_price(:flat, 1.0) + end + + test "non-machinable surcharge consistency" do + normal_price = USPS::FLIRTEngine.metered_price(:letter, 1.0) + non_mach_price = USPS::FLIRTEngine.metered_price(:letter, 1.0, true) + + assert_equal 0.48, non_mach_price - normal_price + end +end \ No newline at end of file diff --git a/test/services/usps/pricing_engine_test.rb b/test/services/usps/pricing_engine_test.rb new file mode 100644 index 00000000..037c89ba --- /dev/null +++ b/test/services/usps/pricing_engine_test.rb @@ -0,0 +1,61 @@ +require "test_helper" + +class USPS::PricingEngineTest < ActiveSupport::TestCase + test "metered letter rates for 2024" do + assert_equal 0.73, USPS::PricingEngine.metered_price(:letter, 1.0) + assert_equal 1.02, USPS::PricingEngine.metered_price(:letter, 2.0) + assert_equal 1.31, USPS::PricingEngine.metered_price(:letter, 3.0) + assert_equal 1.60, USPS::PricingEngine.metered_price(:letter, 3.5) + end + + test "stamp letter rates for 2024" do + assert_equal 0.78, USPS::PricingEngine.domestic_stamp_price(:letter, 1.0) + assert_equal 1.08, USPS::PricingEngine.domestic_stamp_price(:letter, 2.0) + assert_equal 1.38, USPS::PricingEngine.domestic_stamp_price(:letter, 3.0) + assert_equal 1.68, USPS::PricingEngine.domestic_stamp_price(:letter, 3.5) + end + + test "flat rates for 2024" do + assert_equal 1.55, USPS::PricingEngine.metered_price(:flat, 1.0) + assert_equal 1.83, USPS::PricingEngine.metered_price(:flat, 2.0) + assert_equal 2.11, USPS::PricingEngine.metered_price(:flat, 3.0) + end + + test "international letter rates for 2024" do + assert_equal 1.75, USPS::PricingEngine.fcmi_price(:letter, 1.0, "CA") + assert_equal 1.75, USPS::PricingEngine.fcmi_price(:letter, 1.0, "MX") + assert_equal 1.75, USPS::PricingEngine.fcmi_price(:letter, 1.0, "GB") + + assert_equal 2.65, USPS::PricingEngine.fcmi_price(:letter, 2.0, "MX") + assert_equal 3.16, USPS::PricingEngine.fcmi_price(:letter, 2.0, "GB") + end + + test "non-machinable surcharge for 2024" do + normal_price = USPS::PricingEngine.metered_price(:letter, 1.0) + non_mach_price = USPS::PricingEngine.metered_price(:letter, 1.0, true) + + assert_equal 0.48, non_mach_price - normal_price + assert_equal 1.21, non_mach_price + end + + test "stamp price helper method" do + # Test US domestic + assert_equal 0.78, USPS::PricingEngine.stamp_price(:letter, 1.0, "US") + + # Test international + assert_equal 1.75, USPS::PricingEngine.stamp_price(:letter, 1.0, "CA") + assert_equal 1.75, USPS::PricingEngine.stamp_price(:letter, 1.0, "MX") + end + + test "invalid weight raises error" do + assert_raises(ArgumentError) do + USPS::PricingEngine.metered_price(:letter, 4.0) + end + end + + test "invalid type raises error" do + assert_raises(ArgumentError) do + USPS::PricingEngine.metered_price(:package, 1.0) + end + end +end \ No newline at end of file