# USPS Zone Rate Planning ## High-level Questions - Why some Easyship USPS couriers are aleady using ZoneRate? ```ruby couriers = Courier.active.belongs_to_easyship.where(umbrella_name: 'USPS', rate_api_engine: 'ZoneRate') couriers.size #=> 2 couriers.pluck(:admin_name) #=> ["USPS_FirstClassSignatureIB_US", "USPS_FirstClassIB_US"] ``` - What couriers are to switch from third party rates to zone rate? - Zone Delivery Time Implementaion - Where to gather delivery time to make it a file? - admin_names not matching in spreadsheet and production DB ``` $ admin_names_in_production_db = Courier.belongs_to_easyship.where(umbrella_name: 'USPS').pluck(:admin_name) $ admin_names_in_spreadsheet = [ADMIN_NAMES_LISTED_IN_SPREADSHEET_API_ENGINE_TAB] $ admin_names_in_production_db - admin_names_in_spreadsheet #=> [ "USPS_PriorityMailMarketplaceIB_US", "USPS_FirstClassMarketplaceIB_US", "USPS_PMFlatRateMediumPF_US", "USPS_PMFlatRateSmallEnvelopePF_US", "RRD_USPSPriorityParcelDelConDDPLAX_US", "RRD_USPSPriorityParcelDelConDDPEWR_US", "RRD_USPSPriorityParcelDelConDDPORD_US", "USPS_FirstClassElogistics_US" ] ``` - Any special LYOC USPS couriers that don't have Easyship master courier? ## Implementation Plan Overview 1. Add zone_rates and country_rates from Google Drive 2. Refactor DomesticZoneRate for multi-nation compatibility 3. Update UspsZone::Calculator to make base_zone and exception_zone accessible 4. Update `ZoneNameGetter` to process with `priority_mail_or_first_class: false` by default 5. Update couriers' rate_api_engine on ES side ## Detailed Implementation ### Update DomesticZoneRate 1. Change `DomesticZoneRate` to country specific with inheritance, e.g., `ZoneRate` and `ZoneRate::Us` ### Build ZoneRate Records (RC) 1. Run rake to generate zone_rates from Google Drive when rate cards are ready. ``` $ rake zone_rate_generator:update_all_data origin_country_alpha2='US' ``` ### Update FilterService #### #prepare_courier_ids 1. Match `ZoneRate::Us` couriers ids ```ruby def prepare_courier_ids(couriers) zone_rate_us_courier_id = [] couriers.each do |c| next unless c['id'] case c['rate_api_engine'] # ...omitted... when /^ZoneRate::Us/ zone_rate_us_courier_ids << c['id'] # ...omitted... end ``` #### #available_rate_factories 1. Append courier ids to associated rates ``` ruby factories += ['ZoneRate::Us', nil, nil] if @opts[:zone_rate_us_courier_ids].any? ``` ### Update UspsZone::Calculator 1. Change file structure for future addition of other calculators for other countries #### #initialize 1. Remove options #### #process 1. Always return both `base_zone` and `exception_zone` ### Deprecate ZoneNameGetter 1. Use Calculator directly 2. Deprecate ZoneNameGetter ### Update DomesticZoneRate #### #generate_sql 1. Add #zone_name_sql to switch zone_name depending on rate_api_engine ``` ruby def zone_name_sql %{ CASE WHEN couriers.rate_api_engine IN ('ZoneRate::Us::FirstClass', 'ZoneRate::Us::PrioityMail') THEN #{@exception_zone_name} ELSE #{@base_zone_name} END } end ``` 2. Use zone_name_sql in #generate_sql ``` ruby # Before zone_rates.zone_name = '#{zone_name}' AND # After zone_rates.zone_name = #{zone_name_sql} AND ``` #### #price_markup_sql 1. Refactor: ```ruby # Before def price_markup_sql "(zone_rates.base_price * (CASE WHEN couriers.applies_markup = 'true' THEN #{1 + (@opts[:markup_rate] || 0)} ELSE 1 END))" end # After def price_markup_sql %{( zone_rates.base_price * ( CASE WHEN couriers.applies_markup = 'true' THEN #{1 + @opts[:markup_rate].to_f} ELSE 1 END ) )} end ``` ## After Live on Staging/Production ### Update Rate API Engines (ES) 1. Update USPS couriers' rate_api_engine to one of the followings - CountryRate - ZoneRate::Us - ZoneRate::Us::FirstClass - ZoneRate::Us::PriorityMail ``` # TODO: Confirm courier scope and add script after spreadsheet is ready ``` 2. Update rate_api_engine of US couriers (except USPS) with `ZoneRate` rate api engine to one of the followings - ZoneRate::Us::FirstClass - ZoneRate::Us::PriorityMail ``` # TODO: Confirm which rate_api_engine to update ``` ### Remove third-party ThirdPartyRate::{{COURIER_NAMES}} ``` # Phase 2 ``` ## QA ``` # TODO: Confirm courier scope and add script ``` ## References ### Easyship USPS couriers ```ruby Courier.active.belongs_to_easyship.where(umbrella_name: 'USPS').find_each { |e| puts e.admin_name.split('_')[1] } #=> FirstClassInternationalIB PMFlatRateMedium2 PriorityMailSignatureIB ParcelSelectSignatureIB PriorityMailInternationalLaunch PriorityMailExpressLaunch PriorityMailLaunch PriorityMailInternationalExpressIB PriorityMailInternationalIB FirstClassSignatureIB PriorityMailIB PriorityMailExpress PMFlatRateRegionalA1 PMFlatRateGiftCardEnvelope PMFlatRateLargeBoardGame PMIFlatRateGiftCardEnvelope PMIFlatRateLargeVideo PMIFlatRateSmallEnvelope PMIFlatRateWindowEnvelope PMFlatRateSmallEnvelope PMFlatRateWindowEnvelope MediaEfulfilment ParcelSelectEfulfilment PMIFlatRatePaddedEnvelope FirstClassIB ParcelSelect Media PMIFlatRateMedium2 PMFlatRateRegionalB2 PMFlatRateRegionalA2 PMFlatRateSmall PMFlatRateRegionalB1 PMFlatRatePaddedEnvelope PMFlatRateMedium1 PMFlatRateLegalEnvelope PMIFlatRateLargeBoardGame PMFlatRateLarge PMIFlatRateMedium1 PMIFlatRateSmall PMIFlatRateLegalEnvelope PMIFlatRateLarge PMIFlatRateEnvelope PMEFlatRatePaddedEnvelope PMEFlatRateLegalEnvelope PMEIFlatRateLegalEnvelope PMEIFlatRateEnvelope PMEIFlatRatePaddedEnvelope PMFlatRateEnvelope PMEFlatRateEnvelope ```