Skip to content

Commit

Permalink
Merge branch 'main' into B-22227
Browse files Browse the repository at this point in the history
  • Loading branch information
r-mettler authored Feb 25, 2025
2 parents 3f4a27f + caf28a5 commit 41bea85
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 30 deletions.
1 change: 1 addition & 0 deletions migrations/app/migrations_manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,7 @@
20250121153007_update_pricing_proc_to_handle_international_shuttle.up.sql
20250121184450_upd_duty_loc_B-22242.up.sql
20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql
20250123210535_update_re_intl_transit_times_for_ak_hhg.up.sql
20250204162411_updating_create_accessorial_service_item_proc_for_crating.up.sql
20250206173204_add_hawaii_data.up.sql
20250207153450_add_fetch_documents_func.up.sql
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
UPDATE re_intl_transit_times
SET hhg_transit_time = 10
WHERE origin_rate_area_id IN ('b80a00d4-f829-4051-961a-b8945c62c37d','5a27e806-21d4-4672-aa5e-29518f10c0aa')
OR destination_rate_area_id IN ('b80a00d4-f829-4051-961a-b8945c62c37d','5a27e806-21d4-4672-aa5e-29518f10c0aa');

update re_intl_transit_times
SET hhg_transit_time = 20
WHERE origin_rate_area_id IN ('9bb87311-1b29-4f29-8561-8a4c795654d4','635e4b79-342c-4cfc-8069-39c408a2decd')
OR destination_rate_area_id IN ('9bb87311-1b29-4f29-8561-8a4c795654d4','635e4b79-342c-4cfc-8069-39c408a2decd');
72 changes: 72 additions & 0 deletions pkg/factory/address_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,75 @@ func GetTraitAddress4() []Customization {
},
}
}

// GetTraitAddressAKZone1 is an address in Zone 1 of AK
func GetTraitAddressAKZone1() []Customization {

return []Customization{
{
Model: models.Address{
StreetAddress1: "82 Joe Gibbs Rd",
StreetAddress2: models.StringPointer("P.O. Box 1234"),
StreetAddress3: models.StringPointer("c/o Another Person"),
City: "ANCHORAGE",
State: "AK",
PostalCode: "99695",
IsOconus: models.BoolPointer(true),
},
},
}
}

// GetTraitAddressAKZone2 is an address in Zone 2 of Alaska
func GetTraitAddressAKZone2() []Customization {

return []Customization{
{
Model: models.Address{
StreetAddress1: "44 John Riggins Rd",
StreetAddress2: models.StringPointer("P.O. Box 1234"),
StreetAddress3: models.StringPointer("c/o Another Person"),
City: "FAIRBANKS",
State: "AK",
PostalCode: "99703",
IsOconus: models.BoolPointer(true),
},
},
}
}

// GetTraitAddressAKZone3 is an address in Zone 3 of Alaska
func GetTraitAddressAKZone3() []Customization {

return []Customization{
{
Model: models.Address{
StreetAddress1: "26 Clinton Portis Rd",
StreetAddress2: models.StringPointer("P.O. Box 1234"),
StreetAddress3: models.StringPointer("c/o Another Person"),
City: "KODIAK",
State: "AK",
PostalCode: "99697",
IsOconus: models.BoolPointer(true),
},
},
}
}

// GetTraitAddressAKZone4 is an address in Zone 4 of Alaska
func GetTraitAddressAKZone4() []Customization {

return []Customization{
{
Model: models.Address{
StreetAddress1: "8 Alex Ovechkin Rd",
StreetAddress2: models.StringPointer("P.O. Box 1234"),
StreetAddress3: models.StringPointer("c/o Another Person"),
City: "JUNEAU",
State: "AK",
PostalCode: "99801",
IsOconus: models.BoolPointer(true),
},
},
}
}
8 changes: 8 additions & 0 deletions pkg/models/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/gobuffalo/validate/v3"
"github.com/gobuffalo/validate/v3/validators"
"github.com/gofrs/uuid"
"github.com/pkg/errors"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"

Expand Down Expand Up @@ -146,6 +147,13 @@ func (a *Address) LineDisplayFormat() string {
return fmt.Sprintf("%s%s%s, %s, %s %s", a.StreetAddress1, optionalStreetAddress2, optionalStreetAddress3, a.City, a.State, a.PostalCode)
}

func (a *Address) IsAddressAlaska() (bool, error) {
if a == nil {
return false, errors.New("address is nil")
}
return a.State == "AK", nil
}

// NotImplementedCountryCode is the default for unimplemented country code lookup
type NotImplementedCountryCode struct {
message string
Expand Down
31 changes: 31 additions & 0 deletions pkg/models/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,3 +385,34 @@ func (suite *ModelSuite) Test_FetchDutyLocationGblocForAK() {
suite.Equal(string(*gbloc), "MAPK")
})
}

func (suite *ModelSuite) TestIsAddressAlaska() {
var address *m.Address
bool1, err := address.IsAddressAlaska()
suite.Error(err)
suite.Equal("address is nil", err.Error())
suite.Equal(false, bool1)

address = &m.Address{
StreetAddress1: "street 1",
StreetAddress2: m.StringPointer("street 2"),
StreetAddress3: m.StringPointer("street 3"),
City: "city",
PostalCode: "90210",
County: m.StringPointer("County"),
}

bool2, err := address.IsAddressAlaska()
suite.NoError(err)
suite.Equal(m.BoolPointer(false), &bool2)

address.State = "MT"
bool3, err := address.IsAddressAlaska()
suite.NoError(err)
suite.Equal(m.BoolPointer(false), &bool3)

address.State = "AK"
bool4, err := address.IsAddressAlaska()
suite.NoError(err)
suite.Equal(m.BoolPointer(true), &bool4)
}
75 changes: 53 additions & 22 deletions pkg/services/mto_shipment/mto_shipment_updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -1075,7 +1075,7 @@ func (o *mtoShipmentStatusUpdater) setRequiredDeliveryDate(appCtx appcontext.App
pickupLocation = shipment.PickupAddress
deliveryLocation = shipment.DestinationAddress
}
requiredDeliveryDate, calcErr := CalculateRequiredDeliveryDate(appCtx, o.planner, *pickupLocation, *deliveryLocation, *shipment.ScheduledPickupDate, weight.Int(), shipment.MarketCode)
requiredDeliveryDate, calcErr := CalculateRequiredDeliveryDate(appCtx, o.planner, *pickupLocation, *deliveryLocation, *shipment.ScheduledPickupDate, weight.Int(), shipment.MarketCode, shipment.MoveTaskOrderID)
if calcErr != nil {
return calcErr
}
Expand Down Expand Up @@ -1192,18 +1192,7 @@ func reServiceCodesForShipment(shipment models.MTOShipment) []models.ReServiceCo
// CalculateRequiredDeliveryDate function is used to get a distance calculation using the pickup and destination addresses. It then uses
// the value returned to make a fetch on the ghc_domestic_transit_times table and returns a required delivery date
// based on the max_days_transit_time.
func CalculateRequiredDeliveryDate(appCtx appcontext.AppContext, planner route.Planner, pickupAddress models.Address, destinationAddress models.Address, pickupDate time.Time, weight int, marketCode models.MarketCode) (*time.Time, error) {
// Okay, so this is something to get us able to take care of the 20 day condition over in the gdoc linked in this
// story: https://dp3.atlassian.net/browse/MB-1141
// We unfortunately didn't get a lot of guidance regarding vicinity. So for now we're taking zip codes that are the
// explicitly mentioned 20 day cities and those in the same county (that I've manually compiled together here).
// If a move is in that group it adds 20 days, if it's not in that group, but is in Alaska it adds 10 days.
// Else it will not do either of those things.
// The cities for 20 days are: Adak, Kodiak, Juneau, Ketchikan, and Sitka. As well as others in their 'vicinity.'
twentyDayAKZips := [28]string{"99546", "99547", "99591", "99638", "99660", "99685", "99692", "99550", "99608",
"99615", "99619", "99624", "99643", "99644", "99697", "99650", "99801", "99802", "99803", "99811", "99812",
"99950", "99824", "99850", "99901", "99928", "99950", "99835"}

func CalculateRequiredDeliveryDate(appCtx appcontext.AppContext, planner route.Planner, pickupAddress models.Address, destinationAddress models.Address, pickupDate time.Time, weight int, marketCode models.MarketCode, moveID uuid.UUID) (*time.Time, error) {
internationalShipment := marketCode == models.MarketCodeInternational

distance, err := planner.ZipTransitDistance(appCtx, pickupAddress.PostalCode, destinationAddress.PostalCode, internationalShipment)
Expand All @@ -1225,17 +1214,59 @@ func CalculateRequiredDeliveryDate(appCtx appcontext.AppContext, planner route.P
// Add the max transit time to the pickup date to get the new required delivery date
requiredDeliveryDate := pickupDate.AddDate(0, 0, ghcDomesticTransitTime.MaxDaysTransitTime)

// Let's add some days if we're dealing with an alaska shipment.
if destinationAddress.State == "AK" {
for _, zip := range twentyDayAKZips {
if destinationAddress.PostalCode == zip {
// Add an extra 10 days here, so that after we add the 10 for being in AK we wind up with a total of 20
requiredDeliveryDate = requiredDeliveryDate.AddDate(0, 0, 10)
break
destinationIsAlaska, err := destinationAddress.IsAddressAlaska()
if err != nil {
return nil, fmt.Errorf("destination address is nil for move ID: %s", moveID)
}
pickupIsAlaska, err := pickupAddress.IsAddressAlaska()
if err != nil {
return nil, fmt.Errorf("pickup address is nil for move ID: %s", moveID)
}
// Let's add some days if we're dealing with a shipment between CONUS/Alaska
if (destinationIsAlaska || pickupIsAlaska) && !(destinationIsAlaska && pickupIsAlaska) {
var rateAreaID uuid.UUID
var intlTransTime models.InternationalTransitTime

contract, err := models.FetchContractForMove(appCtx, moveID)
if err != nil {
return nil, fmt.Errorf("error fetching contract for move ID: %s", moveID)
}

if destinationIsAlaska {
rateAreaID, err = models.FetchRateAreaID(appCtx.DB(), destinationAddress.ID, &uuid.Nil, contract.ID)
if err != nil {
return nil, fmt.Errorf("error fetching destination rate area id for address ID: %s", destinationAddress.ID)
}
err = appCtx.DB().Where("destination_rate_area_id = $1", rateAreaID).First(&intlTransTime)
if err != nil {
switch err {
case sql.ErrNoRows:
return nil, fmt.Errorf("no international transit time found for destination rate area ID: %s", rateAreaID)
default:
return nil, err
}
}
}

if pickupIsAlaska {
rateAreaID, err = models.FetchRateAreaID(appCtx.DB(), pickupAddress.ID, &uuid.Nil, contract.ID)
if err != nil {
return nil, fmt.Errorf("error fetching pickup rate area id for address ID: %s", pickupAddress.ID)
}
err = appCtx.DB().Where("origin_rate_area_id = $1", rateAreaID).First(&intlTransTime)
if err != nil {
switch err {
case sql.ErrNoRows:
return nil, fmt.Errorf("no international transit time found for pickup rate area ID: %s", rateAreaID)
default:
return nil, err
}
}
}
// Add an extra 10 days for being in AK
requiredDeliveryDate = requiredDeliveryDate.AddDate(0, 0, 10)

if intlTransTime.HhgTransitTime != nil {
requiredDeliveryDate = requiredDeliveryDate.AddDate(0, 0, *intlTransTime.HhgTransitTime)
}
}

// return the value
Expand Down
131 changes: 131 additions & 0 deletions pkg/services/mto_shipment/mto_shipment_updater_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2462,6 +2462,137 @@ func (suite *MTOShipmentServiceSuite) TestUpdateMTOShipmentStatus() {
}
})

suite.Run("Test that we are properly adding days to Alaska shipments", func() {
reContract := testdatagen.FetchOrMakeReContract(suite.DB(), testdatagen.Assertions{})
testdatagen.FetchOrMakeReContractYear(suite.DB(), testdatagen.Assertions{
ReContractYear: models.ReContractYear{
Contract: reContract,
ContractID: reContract.ID,
StartDate: time.Now(),
EndDate: time.Now().Add(time.Hour * 12),
Escalation: 1.0,
EscalationCompounded: 1.0,
},
})
move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil)
appCtx := suite.AppContextForTest()

ghcDomesticTransitTime0LbsUpper := models.GHCDomesticTransitTime{
MaxDaysTransitTime: 12,
WeightLbsLower: 10001,
WeightLbsUpper: 0,
DistanceMilesLower: 0,
DistanceMilesUpper: 10000,
}
verrs, err := suite.DB().ValidateAndCreate(&ghcDomesticTransitTime0LbsUpper)
suite.Assert().False(verrs.HasAny())
suite.NoError(err)

conusAddress := factory.BuildAddress(suite.DB(), nil, []factory.Trait{factory.GetTraitAddress2})
zone1Address := factory.BuildAddress(suite.DB(), nil, []factory.Trait{factory.GetTraitAddressAKZone1})
zone2Address := factory.BuildAddress(suite.DB(), nil, []factory.Trait{factory.GetTraitAddressAKZone2})
zone3Address := factory.BuildAddress(suite.DB(), nil, []factory.Trait{factory.GetTraitAddressAKZone3})
zone4Address := factory.BuildAddress(suite.DB(), nil, []factory.Trait{factory.GetTraitAddressAKZone4})

estimatedWeight := unit.Pound(11000)

testCases10Days := []struct {
pickupLocation models.Address
destinationLocation models.Address
}{
{conusAddress, zone1Address},
{conusAddress, zone2Address},
{zone1Address, conusAddress},
{zone2Address, conusAddress},
}
// adding 22 days; ghcDomesticTransitTime0LbsUpper.MaxDaysTransitTime is 12, plus 10 for Zones 1 and 2
rdd10DaysDate := testdatagen.DateInsidePeakRateCycle.AddDate(0, 0, 22)
for _, testCase := range testCases10Days {
shipment := factory.BuildMTOShipmentMinimal(suite.DB(), []factory.Customization{
{
Model: move,
LinkOnly: true,
},
{
Model: models.MTOShipment{
ShipmentType: models.MTOShipmentTypeHHG,
ScheduledPickupDate: &testdatagen.DateInsidePeakRateCycle,
PrimeEstimatedWeight: &estimatedWeight,
Status: models.MTOShipmentStatusSubmitted,
},
},
{
Model: testCase.pickupLocation,
Type: &factory.Addresses.PickupAddress,
LinkOnly: true,
},
{
Model: testCase.destinationLocation,
Type: &factory.Addresses.DeliveryAddress,
LinkOnly: true,
},
}, nil)
shipmentEtag := etag.GenerateEtag(shipment.UpdatedAt)
_, err = updater.UpdateMTOShipmentStatus(appCtx, shipment.ID, status, nil, nil, shipmentEtag)
suite.NoError(err)

fetchedShipment := models.MTOShipment{}
err = suite.DB().Find(&fetchedShipment, shipment.ID)
suite.NoError(err)
suite.NotNil(fetchedShipment.RequiredDeliveryDate)
suite.Equal(rdd10DaysDate.Format(time.RFC3339), fetchedShipment.RequiredDeliveryDate.Format(time.RFC3339))
}

testCases20Days := []struct {
pickupLocation models.Address
destinationLocation models.Address
}{
{conusAddress, zone3Address},
{conusAddress, zone4Address},
{zone3Address, conusAddress},
{zone4Address, conusAddress},
}
// adding 32 days; ghcDomesticTransitTime0LbsUpper.MaxDaysTransitTime is 12, plus 20 for Zones 3 and 4
rdd20DaysDate := testdatagen.DateInsidePeakRateCycle.AddDate(0, 0, 32)
for _, testCase := range testCases20Days {
shipment := factory.BuildMTOShipmentMinimal(suite.DB(), []factory.Customization{
{
Model: move,
LinkOnly: true,
},
{
Model: models.MTOShipment{
ShipmentType: models.MTOShipmentTypeHHG,
ScheduledPickupDate: &testdatagen.DateInsidePeakRateCycle,
PrimeEstimatedWeight: &estimatedWeight,
Status: models.MTOShipmentStatusSubmitted,
},
},
{
Model: testCase.pickupLocation,
Type: &factory.Addresses.PickupAddress,
LinkOnly: true,
},
{
Model: testCase.destinationLocation,
Type: &factory.Addresses.DeliveryAddress,
LinkOnly: true,
},
}, nil)
shipmentEtag := etag.GenerateEtag(shipment.UpdatedAt)
_, err = updater.UpdateMTOShipmentStatus(appCtx, shipment.ID, status, nil, nil, shipmentEtag)
suite.NoError(err)

fetchedShipment := models.MTOShipment{}
err = suite.DB().Find(&fetchedShipment, shipment.ID)
suite.NoError(err)
suite.NotNil(fetchedShipment.RequiredDeliveryDate)
fmt.Println("fetchedShipment.RequiredDeliveryDate")
fmt.Println(fetchedShipment.RequiredDeliveryDate)
suite.Equal(rdd20DaysDate.Format(time.RFC3339), fetchedShipment.RequiredDeliveryDate.Format(time.RFC3339))
}
})

suite.Run("Cannot set SUBMITTED status on shipment via UpdateMTOShipmentStatus", func() {
setupTestData()

Expand Down
2 changes: 1 addition & 1 deletion pkg/services/mto_shipment/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ func checkPrimeValidationsOnModel(planner route.Planner) validator {
weight = older.NTSRecordedWeight
}
requiredDeliveryDate, err := CalculateRequiredDeliveryDate(appCtx, planner, *latestPickupAddress,
*latestDestinationAddress, *latestSchedPickupDate, weight.Int(), older.MarketCode)
*latestDestinationAddress, *latestSchedPickupDate, weight.Int(), older.MarketCode, older.MoveTaskOrderID)
if err != nil {
verrs.Add("requiredDeliveryDate", err.Error())
}
Expand Down
Loading

0 comments on commit 41bea85

Please sign in to comment.