---
# System prepended metadata

title: USPS Zone Rate Planning

---

# 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
```
