Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/origin/int-b-20928' into i…
Browse files Browse the repository at this point in the history
…nt-b-20928
  • Loading branch information
danieljordan-caci committed Oct 30, 2024
2 parents 6c2c731 + cbf3d0a commit 57d888b
Show file tree
Hide file tree
Showing 69 changed files with 2,662 additions and 420 deletions.
2 changes: 2 additions & 0 deletions migrations/app/migrations_manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1024,4 +1024,6 @@
20241008212243_populate_market_code_on_shipments_table.up.sql
20241009210749_create_view_v_locations.up.sql
20241011201326_changes_for_actual_expense_reimbursement_field.up.sql
20241017183144_add_AK_HI_duty_locations.up.sql
20241024114748_create_gbloc_aors.up.sql
20241029144404_hdt-614-adjust-accomack-county.up.sql
1,083 changes: 1,083 additions & 0 deletions migrations/app/schema/20241017183144_add_AK_HI_duty_locations.up.sql

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Set temp timeout due to large file modification
-- Time is 5 minutes in milliseconds
SET statement_timeout = 300000;
SET lock_timeout = 300000;
SET idle_in_transaction_session_timeout = 300000;

-- Adjust postal code to GBLOC per MMHDT 582
update postal_code_to_gblocs pctg SET gbloc = 'BGNC' FROM us_post_region_cities usprc where pctg.postal_code = usprc.uspr_zip_id and usprc.usprc_county_nm = 'ACCOMACK'
11 changes: 10 additions & 1 deletion pkg/assets/notifications/templates/move_counseled_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@

<p><strong>Next steps for a PPM:</strong>
<ul>
<li>Remember to get legible certified weight tickets for both the empty and full weights for every trip you perform. If you do not upload legible certified weight tickets, your PPM incentive could be affected.</li>
{{if .ActualExpenseReimbursement}}
<li>Please Note: Your PPM has been designated as Actual Expense Reimbursement. This is the standard entitlement for Civilian employees. For uniformed Service Members, your PPM may have been designated as Actual Expense Reimbursement due to failure to receive authorization prior to movement or failure to obtain certified weight tickets. Actual Expense Reimbursement means reimbursement for expenses not to exceed the Government Constructed Cost (GCC).</li>
{{end}}
<li>Remember to get legible certified weight tickets for both the empty and full weights for every trip you perform. If you do not upload legible certified weight tickets, your PPM incentive(or Actual Expense Reimbursement for Civilians) could be affected. Failure to obtain weight tickets will result in losing eligibility to receive your incentive.</li>
<p>Note: To receive allowance for Pro-Gear, you must identify allowable items and provide weight tickets separately for Pro-Gear.</p>
<li>For authorized storage:</li>
<ul>
<li>You will need to get weight ticket(s) for the items you store.</li>
<li>Storage costs cannot be paid in advance.</li>
</ul>
<li>If your counselor approved an Advance Operating Allowance (AOA, or cash advance) for a PPM, log into <a href="{{.MyMoveLink}}">MilMove</a> to download your AOA Packet, and submit it to finance according to the instructions provided by your counselor. If you have been directed to use your government travel charge card (GTCC) for expenses no further action is required.</li>
<li>Once you complete your PPM, log into <a href="{{.MyMoveLink}}">MilMove</a>, upload your receipts and weight tickets, and submit your PPM for review.</li>
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ What this means to you:
If you are doing a Personally Procured Move (PPM), you can start moving your personal property.

Next steps for a PPM:
* Remember to get legible certified weight tickets for both the empty and full weights for every trip you perform. If you do not upload legible certified weight tickets, your PPM incentive could be affected.
{{if .ActualExpenseReimbursement}}
* Please Note: Your PPM has been designated as Actual Expense Reimbursement. This is the standard entitlement for Civilian employees. For uniformed Service Members, your PPM may have been designated as Actual Expense Reimbursement due to failure to receive authorization prior to movement or failure to obtain certified weight tickets. Actual Expense Reimbursement means reimbursement for expenses not to exceed the Government Constructed Cost (GCC).
{{end}}
* Remember to get legible certified weight tickets for both the empty and full weights for every trip you perform. If you do not upload legible certified weight tickets, your PPM incentive(or Actual Expense Reimbursement for Civilians) could be affected. Failure to obtain weight tickets will result in losing eligibility to receive your incentive.
Note: To receive allowance for Pro-Gear, you must identify allowable items and provide weight tickets separately for Pro-Gear.
* For authorized storage:
* You will need to get weight ticket(s) for the items you store.
* Storage costs cannot be paid in advance.
* If your counselor approved an Advance Operating Allowance (AOA, or cash advance) for a PPM, log into MilMove <{{.MyMoveLink}}> to download your AOA Packet, and submit it to finance according to the instructions provided by your counselor. If you have been directed to use your government travel charge card (GTCC) for expenses no further action is required.
* Once you complete your PPM, log into MilMove <{{.MyMoveLink}}>, upload your receipts and weight tickets, and submit your PPM for review.

Expand Down
2 changes: 1 addition & 1 deletion pkg/factory/address_factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ func (suite *FactorySuite) TestBuildAddress() {
City: customCity,
State: customState,
PostalCode: customPostalCode,
IsOconus: models.BoolPointer(false),
County: customCounty,
IsOconus: models.BoolPointer(false),
},
},
{
Expand Down
1 change: 1 addition & 0 deletions pkg/factory/move_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ func BuildMoveWithPPMShipment(db *pop.Connection, customs []Customization, trait
ppmShipment.ShipmentID = mtoShipment.ID

mtoShipment.PPMShipment = &ppmShipment
mtoShipment.ShipmentType = models.MTOShipmentTypePPM
move.MTOShipments = append(move.MTOShipments, mtoShipment)

if db != nil {
Expand Down
14 changes: 12 additions & 2 deletions pkg/handlers/ghcapi/move_task_order_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ func (suite *HandlerSuite) TestGetMoveTaskOrderHandlerIntegration() {
suite.Equal(strfmt.UUID(moveTaskOrder.ID.String()), moveTaskOrderPayload.ID)
suite.Nil(moveTaskOrderPayload.AvailableToPrimeAt)
suite.Nil(moveTaskOrderPayload.ApprovedAt)
// TODO: Check that the *moveTaskOrderPayload.Status is not "canceled"
// suite.False(*moveTaskOrderPayload.IsCanceled)
suite.Equal(strfmt.UUID(moveTaskOrder.OrdersID.String()), moveTaskOrderPayload.OrderID)
suite.NotNil(moveTaskOrderPayload.ReferenceID)
}
Expand Down Expand Up @@ -150,6 +148,18 @@ func (suite *HandlerSuite) TestUpdateMoveTaskOrderHandlerIntegrationSuccess() {
},
},
}, nil)
pickupDate := time.Now()
factory.BuildMTOShipment(suite.DB(), []factory.Customization{
{
Model: move,
LinkOnly: true,
},
{
Model: models.MTOShipment{
RequestedPickupDate: &pickupDate,
},
},
}, nil)

request := httptest.NewRequest("PATCH", "/move-task-orders/{moveID}/status", nil)
requestUser := factory.BuildOfficeUser(nil, nil, nil)
Expand Down
21 changes: 20 additions & 1 deletion pkg/handlers/internalapi/duty_locations.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package internalapi

import (
"context"

"github.com/go-openapi/runtime/middleware"
"github.com/gofrs/uuid"
"go.uber.org/zap"
Expand Down Expand Up @@ -46,7 +48,24 @@ func (h SearchDutyLocationsHandler) Handle(params locationop.SearchDutyLocations
return h.AuditableAppContextFromRequestWithErrors(params.HTTPRequest,
func(appCtx appcontext.AppContext) (middleware.Responder, error) {

locations, err := models.FindDutyLocations(appCtx.DB(), params.Search)
/** Feature Flag - Alaska - Determines if AK can be included/excluded **/
isAlaskaEnabled := false
featureFlagName := "enable_alaska"
flag, err := h.FeatureFlagFetcher().GetBooleanFlagForUser(context.TODO(), appCtx, featureFlagName, map[string]string{})
if err != nil {
appCtx.Logger().Error("Error fetching feature flag", zap.String("featureFlagKey", featureFlagName), zap.Error(err))
} else {
isAlaskaEnabled = flag.Match
}

statesToExclude := make([]string, 0)
if !isAlaskaEnabled {
// HI locations will also be excluded as part of AK embargo
statesToExclude = append(statesToExclude, "AK")
statesToExclude = append(statesToExclude, "HI")
}

locations, err := models.FindDutyLocationsExcludingStates(appCtx.DB(), params.Search, statesToExclude)
if err != nil {
dutyLocationErr := apperror.NewNotFoundError(uuid.Nil, "Finding duty locations")
appCtx.Logger().Error(dutyLocationErr.Error(), zap.Error(err))
Expand Down
133 changes: 110 additions & 23 deletions pkg/handlers/internalapi/duty_locations_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package internalapi

import (
"context"
"net/http/httptest"

"github.com/go-openapi/strfmt"
"github.com/gofrs/uuid"
"github.com/pkg/errors"
"github.com/stretchr/testify/mock"
"go.uber.org/zap"

"github.com/transcom/mymove/pkg/appcontext"
"github.com/transcom/mymove/pkg/auth"
"github.com/transcom/mymove/pkg/factory"
locationop "github.com/transcom/mymove/pkg/gen/internalapi/internaloperations/duty_locations"
"github.com/transcom/mymove/pkg/gen/internalmessages"
"github.com/transcom/mymove/pkg/models"
"github.com/transcom/mymove/pkg/services"
"github.com/transcom/mymove/pkg/services/address"
"github.com/transcom/mymove/pkg/services/mocks"
)

func (suite *HandlerSuite) TestSearchDutyLocationHandler() {
Expand All @@ -25,62 +32,142 @@ func (suite *HandlerSuite) TestSearchDutyLocationHandler() {
}
suite.MustSave(&user)

newAddress := models.Address{
newAKAddress := models.Address{
StreetAddress1: "some address",
City: "city",
State: "CA",
State: "AK",
PostalCode: "12345",
County: "County",
}

newHIAddress := models.Address{
StreetAddress1: "some address",
City: "city",
State: "HI",
PostalCode: "12345",
County: "County",
}
factory.FetchOrBuildCountry(suite.AppContextForTest().DB(), nil, nil)
addressCreator := address.NewAddressCreator()
createdAddress, err := addressCreator.CreateAddress(suite.AppContextForTest(), &newAddress)
createdAKAddress, err := addressCreator.CreateAddress(suite.AppContextForTest(), &newAKAddress)
suite.NoError(err)

location1 := models.DutyLocation{
Name: "First Location",
AddressID: createdAddress.ID,
createdHIAddress, err := addressCreator.CreateAddress(suite.AppContextForTest(), &newHIAddress)
suite.NoError(err)

dutylocationAK := models.DutyLocation{
Name: "HELLOWORLD 1",
AddressID: createdAKAddress.ID,
Affiliation: internalmessages.NewAffiliation(internalmessages.AffiliationAIRFORCE),
}
suite.MustSave(&location1)
suite.MustSave(&dutylocationAK)

location2 := models.DutyLocation{
Name: "Second Location",
AddressID: createdAddress.ID,
dutylocationHI := models.DutyLocation{
Name: "HELLOWORLD 2",
AddressID: createdHIAddress.ID,
Affiliation: internalmessages.NewAffiliation(internalmessages.AffiliationAIRFORCE),
}
suite.MustSave(&location2)
suite.MustSave(&dutylocationHI)

setupTestHandler := func(isAlaskaEnabled bool, isSimulateFeatureFlagError bool) SearchDutyLocationsHandler {
handlerConfig := suite.HandlerConfig()
mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{}

mockGetFlagFunc := func(_ context.Context, _ *zap.Logger, entityID string, key string, _ map[string]string, mockVariant string) (services.FeatureFlag, error) {
return services.FeatureFlag{
Entity: entityID,
Key: key,
Match: isAlaskaEnabled,
Variant: mockVariant,
Namespace: "test",
}, nil
}
if isSimulateFeatureFlagError {
mockFeatureFlagFetcher.On("GetBooleanFlagForUser",
mock.Anything,
mock.AnythingOfType("*appcontext.appContext"),
mock.AnythingOfType("string"),
mock.Anything,
).Return(services.FeatureFlag{}, errors.New("Some error"))
handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher)
} else {
mockFeatureFlagFetcher.On("GetBooleanFlagForUser",
mock.Anything,
mock.AnythingOfType("*appcontext.appContext"),
mock.AnythingOfType("string"),
mock.Anything,
).Return(func(ctx context.Context, appCtx appcontext.AppContext, key string, flagContext map[string]string) (services.FeatureFlag, error) {
return mockGetFlagFunc(ctx, appCtx.Logger(), "user@example.com", key, flagContext, "")
})
}
handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher)
handler := SearchDutyLocationsHandler{handlerConfig}
return handler
}

req := httptest.NewRequest("GET", "/duty_locations", nil)

// Make sure the context contains the auth values
session := &auth.Session{
ApplicationName: auth.MilApp,
UserID: user.ID,
IDToken: "fake token",
}
ctx := auth.SetSessionInRequestContext(req, session)

newSearchParams := locationop.SearchDutyLocationsParams{
HTTPRequest: req.WithContext(ctx),
Search: "first",
Search: "helloworld",
}

handler := SearchDutyLocationsHandler{suite.HandlerConfig()}
response := handler.Handle(newSearchParams)
//////////////////////////////////////////////////////////////
// test when alaska is enabled
//////////////////////////////////////////////////////////////
var handlerAlaska = setupTestHandler(true, false)

var responseAlaska = handlerAlaska.Handle(newSearchParams)

// Assert we got back the 201 response
searchResponse := response.(*locationop.SearchDutyLocationsOK)
locationPayloads := searchResponse.Payload
var searchResponseAlaska = responseAlaska.(*locationop.SearchDutyLocationsOK)
var locationPayloadsAlaska = searchResponseAlaska.Payload

suite.NoError(locationPayloads.Validate(strfmt.Default))
suite.NoError(locationPayloadsAlaska.Validate(strfmt.Default))

if len(locationPayloads) != 1 {
t.Errorf("Should have 1 responses, got %v", len(locationPayloads))
if len(locationPayloadsAlaska) != 2 {
t.Errorf("Should have 2 responses, got %v", len(locationPayloadsAlaska))
}

if *locationPayloads[0].Name != "First Location" {
t.Errorf("Location name should have been \"First Location \", got %v", locationPayloads[0].Name)
//////////////////////////////////////////////////////////////
// test when alaska is not enabled
//////////////////////////////////////////////////////////////
handlerAlaska = setupTestHandler(false, false)

responseAlaska = handlerAlaska.Handle(newSearchParams)

searchResponseAlaska = responseAlaska.(*locationop.SearchDutyLocationsOK)
locationPayloadsAlaska = searchResponseAlaska.Payload

suite.NoError(locationPayloadsAlaska.Validate(strfmt.Default))

// should return zero matches
if len(locationPayloadsAlaska) != 0 {
t.Errorf("Should have 0 responses, got %v", len(locationPayloadsAlaska))
}

//////////////////////////////////////////////////////////////
// test when FF retrieval throws an error
//////////////////////////////////////////////////////////////
handlerAlaska = setupTestHandler(true, true)

responseAlaska = handlerAlaska.Handle(newSearchParams)

// Assert we got back the 201 response
searchResponseAlaska = responseAlaska.(*locationop.SearchDutyLocationsOK)
locationPayloadsAlaska = searchResponseAlaska.Payload

suite.NoError(locationPayloadsAlaska.Validate(strfmt.Default))

// simulating FeatureFlagFetcher().GetBooleanFlagForUser
// throws error, defaults to FALSE returning 0 matches.
if len(locationPayloadsAlaska) != 0 {
t.Errorf("Should have 0 responses because error sets flag to false, got %v", len(locationPayloadsAlaska))
}
}
44 changes: 22 additions & 22 deletions pkg/handlers/internalapi/internal/payloads/model_to_payload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,6 @@ import (
"github.com/transcom/mymove/pkg/models"
)

func (suite *PayloadsSuite) TestMarketCode() {
suite.Run("returns nil when marketCode is nil", func() {
var marketCode *models.MarketCode = nil
result := MarketCode(marketCode)
suite.Equal(result, "")
})

suite.Run("returns string when marketCode is not nil", func() {
marketCodeDomestic := models.MarketCodeDomestic
result := MarketCode(&marketCodeDomestic)
suite.NotNil(result, "Expected result to not be nil when marketCode is not nil")
suite.Equal("d", result, "Expected result to be 'd' for domestic market code")
})

suite.Run("returns string when marketCode is international", func() {
marketCodeInternational := models.MarketCodeInternational
result := MarketCode(&marketCodeInternational)
suite.NotNil(result, "Expected result to not be nil when marketCode is not nil")
suite.Equal("i", result, "Expected result to be 'i' for international market code")
})
}

func (suite *PayloadsSuite) TestFetchPPMShipment() {

ppmShipmentID, _ := uuid.NewV4()
Expand Down Expand Up @@ -88,3 +66,25 @@ func (suite *PayloadsSuite) TestFetchPPMShipment() {
suite.True(*returnedPPMShipment.IsActualExpenseReimbursement)
})
}

func (suite *PayloadsSuite) TestMarketCode() {
suite.Run("returns nil when marketCode is nil", func() {
var marketCode *models.MarketCode = nil
result := MarketCode(marketCode)
suite.Equal(result, "")
})

suite.Run("returns string when marketCode is not nil", func() {
marketCodeDomestic := models.MarketCodeDomestic
result := MarketCode(&marketCodeDomestic)
suite.NotNil(result, "Expected result to not be nil when marketCode is not nil")
suite.Equal("d", result, "Expected result to be 'd' for domestic market code")
})

suite.Run("returns string when marketCode is international", func() {
marketCodeInternational := models.MarketCodeInternational
result := MarketCode(&marketCodeInternational)
suite.NotNil(result, "Expected result to not be nil when marketCode is not nil")
suite.Equal("i", result, "Expected result to be 'i' for international market code")
})
}
Loading

0 comments on commit 57d888b

Please sign in to comment.