From e3ecb66ef4be2889a66631b161808c940164c9fe Mon Sep 17 00:00:00 2001 From: Paul Stonebraker Date: Fri, 24 Jan 2025 19:22:05 +0000 Subject: [PATCH 1/4] add extra days for shipments to or from alaska --- migrations/app/migrations_manifest.txt | 1 + ...te_re_intl_transit_times_for_ak_hhg.up.sql | 9 ++ pkg/factory/address_factory.go | 72 ++++++++++ .../mto_shipment/mto_shipment_updater.go | 67 ++++++--- .../mto_shipment/mto_shipment_updater_test.go | 131 ++++++++++++++++++ pkg/services/mto_shipment/rules.go | 2 +- .../mto_shipment/shipment_approver.go | 2 +- scripts/db-truncate | 2 +- 8 files changed, 261 insertions(+), 25 deletions(-) create mode 100644 migrations/app/schema/20250123210535_update_re_intl_transit_times_for_ak_hhg.up.sql diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index b1d24b20447..df6995239d2 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1075,3 +1075,4 @@ 20250113201232_update_estimated_pricing_procs_add_is_peak_func.up.sql 20250116200912_disable_homesafe_stg_cert.up.sql 20250120144247_update_pricing_proc_to_use_110_percent_weight.up.sql +20250123210535_update_re_intl_transit_times_for_ak_hhg.up.sql diff --git a/migrations/app/schema/20250123210535_update_re_intl_transit_times_for_ak_hhg.up.sql b/migrations/app/schema/20250123210535_update_re_intl_transit_times_for_ak_hhg.up.sql new file mode 100644 index 00000000000..fb67d5fee8b --- /dev/null +++ b/migrations/app/schema/20250123210535_update_re_intl_transit_times_for_ak_hhg.up.sql @@ -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'); \ No newline at end of file diff --git a/pkg/factory/address_factory.go b/pkg/factory/address_factory.go index 27d92999d00..ad4ce46507f 100644 --- a/pkg/factory/address_factory.go +++ b/pkg/factory/address_factory.go @@ -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), + }, + }, + } +} diff --git a/pkg/services/mto_shipment/mto_shipment_updater.go b/pkg/services/mto_shipment/mto_shipment_updater.go index e83e6a6e223..4065e58ffba 100644 --- a/pkg/services/mto_shipment/mto_shipment_updater.go +++ b/pkg/services/mto_shipment/mto_shipment_updater.go @@ -1073,7 +1073,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 } @@ -1190,18 +1190,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 // Get a distance calculation between pickup and destination addresses. distance, err := planner.ZipTransitDistance(appCtx, pickupAddress.PostalCode, destinationAddress.PostalCode, false, internationalShipment) @@ -1223,17 +1212,51 @@ 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 + // Let's add some days if we're dealing with a shipment between CONUS/Alaska + if (destinationAddress.State == "AK" || pickupAddress.State == "AK") && !(destinationAddress.State == "AK" && pickupAddress.State == "AK") { + 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 destinationAddress.State == "AK" { + 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 pickupAddress.State == "AK" { + 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 diff --git a/pkg/services/mto_shipment/mto_shipment_updater_test.go b/pkg/services/mto_shipment/mto_shipment_updater_test.go index 63d4af96dc8..02c7408e1bd 100644 --- a/pkg/services/mto_shipment/mto_shipment_updater_test.go +++ b/pkg/services/mto_shipment/mto_shipment_updater_test.go @@ -2466,6 +2466,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() diff --git a/pkg/services/mto_shipment/rules.go b/pkg/services/mto_shipment/rules.go index 0fe7e481ebc..604da6a12f0 100644 --- a/pkg/services/mto_shipment/rules.go +++ b/pkg/services/mto_shipment/rules.go @@ -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()) } diff --git a/pkg/services/mto_shipment/shipment_approver.go b/pkg/services/mto_shipment/shipment_approver.go index b285684a62b..b2d75e50ebb 100644 --- a/pkg/services/mto_shipment/shipment_approver.go +++ b/pkg/services/mto_shipment/shipment_approver.go @@ -213,7 +213,7 @@ func (f *shipmentApprover) setRequiredDeliveryDate(appCtx appcontext.AppContext, deliveryLocation = shipment.DestinationAddress weight = shipment.PrimeEstimatedWeight.Int() } - requiredDeliveryDate, calcErr := CalculateRequiredDeliveryDate(appCtx, f.planner, *pickupLocation, *deliveryLocation, *shipment.ScheduledPickupDate, weight, shipment.MarketCode) + requiredDeliveryDate, calcErr := CalculateRequiredDeliveryDate(appCtx, f.planner, *pickupLocation, *deliveryLocation, *shipment.ScheduledPickupDate, weight, shipment.MarketCode, shipment.MoveTaskOrderID) if calcErr != nil { return calcErr } diff --git a/scripts/db-truncate b/scripts/db-truncate index 341412b4ab0..53b929432dd 100755 --- a/scripts/db-truncate +++ b/scripts/db-truncate @@ -15,7 +15,7 @@ BEGIN 'ports','port_locations', 're_fsc_multipliers', 'ghc_diesel_fuel_prices', 're_zip3s','zip3_distances', 're_contracts', 're_domestic_service_areas', 're_intl_prices', 're_intl_other_prices', 're_domestic_linehaul_prices', - 're_domestic_service_area_prices', 're_domestic_other_prices')) LOOP + 're_domestic_service_area_prices', 're_domestic_other_prices', 'pay_grades', 'hhg_allowances')) LOOP EXECUTE 'TRUNCATE TABLE ' || quote_ident(r.tablename) || ' CASCADE'; END LOOP; END \$\$; From 9f574deaf187dc6a1c3235fce45e6679c5f203b4 Mon Sep 17 00:00:00 2001 From: Paul Stonebraker Date: Mon, 27 Jan 2025 14:40:10 +0000 Subject: [PATCH 2/4] add IsAddressAlaska helper --- pkg/models/address.go | 4 ++++ pkg/models/address_test.go | 22 +++++++++++++++++++ .../mto_shipment/mto_shipment_updater.go | 6 ++--- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/pkg/models/address.go b/pkg/models/address.go index d89a163c9aa..e4109c51995 100644 --- a/pkg/models/address.go +++ b/pkg/models/address.go @@ -146,6 +146,10 @@ 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 { + return a.State == "AK" +} + // NotImplementedCountryCode is the default for unimplemented country code lookup type NotImplementedCountryCode struct { message string diff --git a/pkg/models/address_test.go b/pkg/models/address_test.go index f2fbb5bf45c..33d9f596732 100644 --- a/pkg/models/address_test.go +++ b/pkg/models/address_test.go @@ -190,3 +190,25 @@ func (suite *ModelSuite) TestPartialAddressFormat() { suite.Equal("street 1, city, state 90210", formattedAddress) } + +func (suite *ModelSuite) TestIsAddressAlaska() { + address := &m.Address{ + StreetAddress1: "street 1", + StreetAddress2: m.StringPointer("street 2"), + StreetAddress3: m.StringPointer("street 3"), + City: "city", + PostalCode: "90210", + County: m.StringPointer("County"), + } + + bool1 := address.IsAddressAlaska() + suite.Equal(m.BoolPointer(false), &bool1) + + address.State = "MT" + bool2 := address.IsAddressAlaska() + suite.Equal(m.BoolPointer(false), &bool2) + + address.State = "AK" + bool3 := address.IsAddressAlaska() + suite.Equal(m.BoolPointer(true), &bool3) +} diff --git a/pkg/services/mto_shipment/mto_shipment_updater.go b/pkg/services/mto_shipment/mto_shipment_updater.go index 4065e58ffba..b188d582c0e 100644 --- a/pkg/services/mto_shipment/mto_shipment_updater.go +++ b/pkg/services/mto_shipment/mto_shipment_updater.go @@ -1213,7 +1213,7 @@ func CalculateRequiredDeliveryDate(appCtx appcontext.AppContext, planner route.P requiredDeliveryDate := pickupDate.AddDate(0, 0, ghcDomesticTransitTime.MaxDaysTransitTime) // Let's add some days if we're dealing with a shipment between CONUS/Alaska - if (destinationAddress.State == "AK" || pickupAddress.State == "AK") && !(destinationAddress.State == "AK" && pickupAddress.State == "AK") { + if (destinationAddress.IsAddressAlaska() || pickupAddress.IsAddressAlaska()) && !(destinationAddress.IsAddressAlaska() && pickupAddress.IsAddressAlaska()) { var rateAreaID uuid.UUID var intlTransTime models.InternationalTransitTime @@ -1222,7 +1222,7 @@ func CalculateRequiredDeliveryDate(appCtx appcontext.AppContext, planner route.P return nil, fmt.Errorf("error fetching contract for move ID: %s", moveID) } - if destinationAddress.State == "AK" { + if destinationAddress.IsAddressAlaska() { 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) @@ -1238,7 +1238,7 @@ func CalculateRequiredDeliveryDate(appCtx appcontext.AppContext, planner route.P } } - if pickupAddress.State == "AK" { + if pickupAddress.IsAddressAlaska() { 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) From 91b885b3f1dee02397f3056ca1a0574f4be928b6 Mon Sep 17 00:00:00 2001 From: Paul Stonebraker Date: Mon, 27 Jan 2025 15:19:52 +0000 Subject: [PATCH 3/4] make the function more better --- pkg/models/address.go | 8 +++++-- pkg/models/address_test.go | 23 +++++++++++++------ .../mto_shipment/mto_shipment_updater.go | 14 ++++++++--- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/pkg/models/address.go b/pkg/models/address.go index e4109c51995..b7a9af15731 100644 --- a/pkg/models/address.go +++ b/pkg/models/address.go @@ -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" @@ -146,8 +147,11 @@ 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 { - return a.State == "AK" +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 diff --git a/pkg/models/address_test.go b/pkg/models/address_test.go index 33d9f596732..c2c84dbeaa8 100644 --- a/pkg/models/address_test.go +++ b/pkg/models/address_test.go @@ -192,7 +192,13 @@ func (suite *ModelSuite) TestPartialAddressFormat() { } func (suite *ModelSuite) TestIsAddressAlaska() { - address := &m.Address{ + 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"), @@ -201,14 +207,17 @@ func (suite *ModelSuite) TestIsAddressAlaska() { County: m.StringPointer("County"), } - bool1 := address.IsAddressAlaska() - suite.Equal(m.BoolPointer(false), &bool1) + bool2, err := address.IsAddressAlaska() + suite.NoError(err) + suite.Equal(m.BoolPointer(false), &bool2) address.State = "MT" - bool2 := address.IsAddressAlaska() - suite.Equal(m.BoolPointer(false), &bool2) + bool3, err := address.IsAddressAlaska() + suite.NoError(err) + suite.Equal(m.BoolPointer(false), &bool3) address.State = "AK" - bool3 := address.IsAddressAlaska() - suite.Equal(m.BoolPointer(true), &bool3) + bool4, err := address.IsAddressAlaska() + suite.NoError(err) + suite.Equal(m.BoolPointer(true), &bool4) } diff --git a/pkg/services/mto_shipment/mto_shipment_updater.go b/pkg/services/mto_shipment/mto_shipment_updater.go index b188d582c0e..cd95cd0f47a 100644 --- a/pkg/services/mto_shipment/mto_shipment_updater.go +++ b/pkg/services/mto_shipment/mto_shipment_updater.go @@ -1212,8 +1212,16 @@ 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) + 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 (destinationAddress.IsAddressAlaska() || pickupAddress.IsAddressAlaska()) && !(destinationAddress.IsAddressAlaska() && pickupAddress.IsAddressAlaska()) { + if (destinationIsAlaska || pickupIsAlaska) && !(destinationIsAlaska && pickupIsAlaska) { var rateAreaID uuid.UUID var intlTransTime models.InternationalTransitTime @@ -1222,7 +1230,7 @@ func CalculateRequiredDeliveryDate(appCtx appcontext.AppContext, planner route.P return nil, fmt.Errorf("error fetching contract for move ID: %s", moveID) } - if destinationAddress.IsAddressAlaska() { + 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) @@ -1238,7 +1246,7 @@ func CalculateRequiredDeliveryDate(appCtx appcontext.AppContext, planner route.P } } - if pickupAddress.IsAddressAlaska() { + 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) From ba50c89a58e71104a155fcc375cbb5843a8a30d1 Mon Sep 17 00:00:00 2001 From: Paul Stonebraker Date: Mon, 24 Feb 2025 13:24:51 -0700 Subject: [PATCH 4/4] update migration manifest --- migrations/app/migrations_manifest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index cb92dbc05dc..ac63e791ed7 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1087,11 +1087,11 @@ 20250114164752_add_ppm_estimated_incentive_proc.up.sql 20250116200912_disable_homesafe_stg_cert.up.sql 20250120144247_update_pricing_proc_to_use_110_percent_weight.up.sql -20250123210535_update_re_intl_transit_times_for_ak_hhg.up.sql 20250120214107_add_international_ntsr_service_items.up.sql 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 20250210175754_B22451_update_dest_queue_to_consider_sit_extensions.up.sql