diff --git a/pkg/factory/move_factory.go b/pkg/factory/move_factory.go index 15b0fb4154c..8561df50127 100644 --- a/pkg/factory/move_factory.go +++ b/pkg/factory/move_factory.go @@ -35,14 +35,6 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode closeoutOffice = BuildTransportationOffice(db, tempCloseoutOfficeCustoms, nil) } - var counselingOffice models.TransportationOffice - tempCounselingOfficeCustoms := customs - counselingOfficeResult := findValidCustomization(customs, TransportationOffices.CounselingOffice) - if counselingOfficeResult != nil { - tempCounselingOfficeCustoms = convertCustomizationInList(tempCounselingOfficeCustoms, TransportationOffices.CounselingOffice, TransportationOffice) - counselingOffice = BuildTransportationOffice(db, tempCounselingOfficeCustoms, nil) - } - var scAssignedUser models.OfficeUser tempSCAssignedUserCustoms := customs scAssignedUserResult := findValidCustomization(customs, OfficeUsers.SCAssignedUser) @@ -51,6 +43,30 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode scAssignedUser = BuildOfficeUser(db, tempSCAssignedUserCustoms, nil) } + var tooAssignedUser models.OfficeUser + tempTOOAssignedUserCustoms := customs + tooAssignedUserResult := findValidCustomization(customs, OfficeUsers.TOOAssignedUser) + if tooAssignedUserResult != nil { + tempTOOAssignedUserCustoms = convertCustomizationInList(tempTOOAssignedUserCustoms, OfficeUsers.TOOAssignedUser, OfficeUser) + tooAssignedUser = BuildOfficeUser(db, tempTOOAssignedUserCustoms, nil) + } + + var tioAssignedUser models.OfficeUser + tempTIOAssignedUserCustoms := customs + tioAssignedUserResult := findValidCustomization(customs, OfficeUsers.TIOAssignedUser) + if tioAssignedUserResult != nil { + tempTIOAssignedUserCustoms = convertCustomizationInList(tempTIOAssignedUserCustoms, OfficeUsers.TIOAssignedUser, OfficeUser) + tioAssignedUser = BuildOfficeUser(db, tempTIOAssignedUserCustoms, nil) + } + + var counselingOffice models.TransportationOffice + tempCounselingOfficeCustoms := customs + counselingOfficeResult := findValidCustomization(customs, TransportationOffices.CounselingOffice) + if counselingOfficeResult != nil { + tempCounselingOfficeCustoms = convertCustomizationInList(tempCounselingOfficeCustoms, TransportationOffices.CounselingOffice, TransportationOffice) + counselingOffice = BuildTransportationOffice(db, tempCounselingOfficeCustoms, nil) + } + var defaultReferenceID string var err error if db != nil { @@ -89,6 +105,16 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode move.CloseoutOfficeID = &closeoutOffice.ID } + if tooAssignedUserResult != nil { + move.TOOAssignedUser = &tooAssignedUser + move.TOOAssignedID = &tooAssignedUser.ID + } + + if tioAssignedUserResult != nil { + move.TIOAssignedUser = &tioAssignedUser + move.TIOAssignedID = &tioAssignedUser.ID + } + if counselingOfficeResult != nil { move.CounselingOffice = &counselingOffice move.CounselingOfficeID = &counselingOffice.ID diff --git a/pkg/factory/move_factory_test.go b/pkg/factory/move_factory_test.go index 306e7f6ee44..9d6f39d498f 100644 --- a/pkg/factory/move_factory_test.go +++ b/pkg/factory/move_factory_test.go @@ -285,6 +285,59 @@ func (suite *FactorySuite) TestBuildMove() { suite.NotEmpty(move.MTOShipments) suite.Equal(models.MTOShipmentStatusSubmitted, move.MTOShipments[0].Status) }) + suite.Run("Successful creation of a move with an assigned SC", func() { + officeUser := BuildOfficeUserWithRoles(suite.DB(), nil, []roles.RoleType{roles.RoleTypeServicesCounselor}) + + move := BuildMoveWithShipment(suite.DB(), []Customization{ + { + Model: models.Move{ + Status: models.MoveStatusAPPROVED, + }, + }, + { + Model: officeUser, + LinkOnly: true, + Type: &OfficeUsers.SCAssignedUser, + }, + }, nil) + suite.Equal(officeUser.ID, *move.SCAssignedID) + }) + + suite.Run("Successful creation of a move with an assigned TOO", func() { + officeUser := BuildOfficeUserWithRoles(suite.DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) + + move := BuildMoveWithShipment(suite.DB(), []Customization{ + { + Model: models.Move{ + Status: models.MoveStatusAPPROVED, + }, + }, + { + Model: officeUser, + LinkOnly: true, + Type: &OfficeUsers.TOOAssignedUser, + }, + }, nil) + suite.Equal(officeUser.ID, *move.TOOAssignedID) + }) + + suite.Run("Successful creation of a move with an assigned TIO", func() { + officeUser := BuildOfficeUserWithRoles(suite.DB(), nil, []roles.RoleType{roles.RoleTypeTIO}) + + move := BuildMoveWithShipment(suite.DB(), []Customization{ + { + Model: models.Move{ + Status: models.MoveStatusAPPROVED, + }, + }, + { + Model: officeUser, + LinkOnly: true, + Type: &OfficeUsers.TIOAssignedUser, + }, + }, nil) + suite.Equal(officeUser.ID, *move.TIOAssignedID) + }) suite.Run("Successful creation of customized move with shipment", func() { // Under test: BuildMoveWithShipment diff --git a/pkg/factory/shared.go b/pkg/factory/shared.go index 1f62571853f..037f2d352d5 100644 --- a/pkg/factory/shared.go +++ b/pkg/factory/shared.go @@ -274,11 +274,15 @@ var TransportationOffices = transportationOfficeGroup{ } type officeUserGroup struct { - SCAssignedUser CustomType + SCAssignedUser CustomType + TIOAssignedUser CustomType + TOOAssignedUser CustomType } var OfficeUsers = officeUserGroup{ - SCAssignedUser: "SCAssignedUser", + SCAssignedUser: "SCAssignedUser", + TIOAssignedUser: "TIOAssignedUser", + TOOAssignedUser: "TOOAssignedUser", } // uploadGroup is a grouping of all the upload related fields diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go index cc62055b3ae..e170710f29f 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go @@ -14,6 +14,7 @@ import ( "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/models/roles" "github.com/transcom/mymove/pkg/storage/test" + "github.com/transcom/mymove/pkg/unit" ) func TestOrder(_ *testing.T) { @@ -21,6 +22,90 @@ func TestOrder(_ *testing.T) { Order(order) } +func (suite *PayloadsSuite) TestOrderWithMove() { + move := factory.BuildMove(suite.DB(), nil, nil) + moves := models.Moves{} + moves = append(moves, move) + order := factory.BuildOrder(nil, []factory.Customization{ + { + Model: models.Order{ + ID: uuid.Must(uuid.NewV4()), + HasDependents: *models.BoolPointer(true), + Moves: moves, + }, + }, + }, nil) + Order(&order) +} + +func (suite *PayloadsSuite) TestBoatShipment() { + boat := factory.BuildBoatShipment(suite.DB(), nil, nil) + boatShipment := BoatShipment(nil, &boat) + suite.NotNil(boatShipment) + +} + +func (suite *PayloadsSuite) TestMobileHomeShipment() { + mobileHome := factory.BuildMobileHomeShipment(suite.DB(), nil, nil) + mobileHomeShipment := MobileHomeShipment(nil, &mobileHome) + suite.NotNil(mobileHomeShipment) +} + +func (suite *PayloadsSuite) TestMovingExpense() { + contractExpense := models.MovingExpenseReceiptTypeContractedExpense + weightStored := 2000 + sitLocation := models.SITLocationTypeDestination + sitReimburseableAmount := 500 + + movingExpense := models.MovingExpense{ + PPMShipmentID: uuid.Must(uuid.NewV4()), + DocumentID: uuid.Must(uuid.NewV4()), + MovingExpenseType: &contractExpense, + Reason: models.StringPointer("no good"), + SITStartDate: models.TimePointer(time.Now()), + SITEndDate: models.TimePointer(time.Now()), + WeightStored: (*unit.Pound)(&weightStored), + SITLocation: &sitLocation, + SITReimburseableAmount: (*unit.Cents)(&sitReimburseableAmount), + } + movingExpenseValues := MovingExpense(nil, &movingExpense) + suite.NotNil(movingExpenseValues) +} + +func (suite *PayloadsSuite) TestMovingExpenses() { + contractExpense := models.MovingExpenseReceiptTypeContractedExpense + weightStored := 2000 + sitLocation := models.SITLocationTypeDestination + sitReimburseableAmount := 500 + movingExpenses := models.MovingExpenses{} + + movingExpense := models.MovingExpense{ + PPMShipmentID: uuid.Must(uuid.NewV4()), + DocumentID: uuid.Must(uuid.NewV4()), + MovingExpenseType: &contractExpense, + Reason: models.StringPointer("no good"), + SITStartDate: models.TimePointer(time.Now()), + SITEndDate: models.TimePointer(time.Now()), + WeightStored: (*unit.Pound)(&weightStored), + SITLocation: &sitLocation, + SITReimburseableAmount: (*unit.Cents)(&sitReimburseableAmount), + } + movingExpenseTwo := models.MovingExpense{ + PPMShipmentID: uuid.Must(uuid.NewV4()), + DocumentID: uuid.Must(uuid.NewV4()), + MovingExpenseType: &contractExpense, + Reason: models.StringPointer("no good"), + SITStartDate: models.TimePointer(time.Now()), + SITEndDate: models.TimePointer(time.Now()), + WeightStored: (*unit.Pound)(&weightStored), + SITLocation: &sitLocation, + SITReimburseableAmount: (*unit.Cents)(&sitReimburseableAmount), + } + movingExpenses = append(movingExpenses, movingExpense, movingExpenseTwo) + movingExpensesValue := MovingExpenses(nil, movingExpenses) + suite.NotNil(movingExpensesValue) +} + // TestMove makes sure zero values/optional fields are handled func TestMove(t *testing.T) { _, err := Move(&models.Move{}, &test.FakeS3Storage{}) @@ -661,6 +746,81 @@ func (suite *PayloadsSuite) TestCreateCustomer() { }) } +func (suite *PayloadsSuite) TestMoveTaskOrder() { + move := factory.BuildMove(suite.DB(), nil, nil) + moveTaskOrder := MoveTaskOrder(&move) + suite.NotNil(moveTaskOrder) +} + +func (suite *PayloadsSuite) TestTransportationOffice() { + office := factory.BuildTransportationOffice(suite.DB(), []factory.Customization{ + { + Model: models.TransportationOffice{ + ID: uuid.Must(uuid.NewV4()), + }, + }}, nil) + transportationOffice := TransportationOffice(&office) + suite.NotNil(transportationOffice) +} +func (suite *PayloadsSuite) TestTransportationOffices() { + office := factory.BuildTransportationOffice(suite.DB(), []factory.Customization{ + { + Model: models.TransportationOffice{ + ID: uuid.Must(uuid.NewV4()), + }, + }}, nil) + officeTwo := factory.BuildTransportationOffice(suite.DB(), []factory.Customization{ + { + Model: models.TransportationOffice{ + ID: uuid.Must(uuid.NewV4()), + }, + }}, nil) + transportationOfficeList := models.TransportationOffices{} + transportationOfficeList = append(transportationOfficeList, office, officeTwo) + value := TransportationOffices(transportationOfficeList) + suite.NotNil(value) +} +func (suite *PayloadsSuite) TestListMove() { + + marines := models.AffiliationMARINES + listMove := ListMove(nil) + + suite.Nil(listMove) + moveUSMC := factory.BuildMove(suite.DB(), []factory.Customization{ + { + Model: models.ServiceMember{ + Affiliation: &marines, + }, + }, + }, nil) + + listMove = ListMove(&moveUSMC) + suite.NotNil(listMove) +} + +func (suite *PayloadsSuite) TestListMoves() { + list := models.Moves{} + + marines := models.AffiliationMARINES + spaceForce := models.AffiliationSPACEFORCE + moveUSMC := factory.BuildMove(suite.DB(), []factory.Customization{ + { + Model: models.ServiceMember{ + Affiliation: &marines, + }, + }, + }, nil) + moveSF := factory.BuildMove(suite.DB(), []factory.Customization{ + { + Model: models.ServiceMember{ + Affiliation: &spaceForce, + }, + }, + }, nil) + list = append(list, moveUSMC, moveSF) + value := ListMoves(&list) + suite.NotNil(value) +} func (suite *PayloadsSuite) TestSearchMoves() { appCtx := suite.AppContextForTest() @@ -672,9 +832,8 @@ func (suite *PayloadsSuite) TestSearchMoves() { }, }, }, nil) - moves := models.Moves{moveUSMC} - suite.Run("Success - Returns a ghcmessages Upload payload from Upload Struct", func() { + suite.Run("Success - Returns a ghcmessages Upload payload from Upload Struct Marine move with no shipments", func() { payload := SearchMoves(appCtx, moves) suite.IsType(payload, &ghcmessages.SearchMoves{}) diff --git a/pkg/handlers/ghcapi/payment_request_test.go b/pkg/handlers/ghcapi/payment_request_test.go index 3d2f116accc..17136806724 100644 --- a/pkg/handlers/ghcapi/payment_request_test.go +++ b/pkg/handlers/ghcapi/payment_request_test.go @@ -228,17 +228,25 @@ func (suite *HandlerSuite) TestGetPaymentRequestsForMoveHandler() { func (suite *HandlerSuite) TestUpdatePaymentRequestStatusHandler() { paymentRequestID, _ := uuid.FromString("00000000-0000-0000-0000-000000000001") - officeUserUUID, _ := uuid.NewV4() setupTestData := func() models.OfficeUser { - officeUser := factory.BuildOfficeUser(nil, []factory.Customization{ - {Model: models.OfficeUser{ - ID: officeUserUUID, - }}, + + transportationOffice := factory.BuildTransportationOffice(suite.DB(), []factory.Customization{ + { + Model: models.TransportationOffice{ + ProvidesCloseout: true, + }, + }, }, nil) - officeUser.User.Roles = append(officeUser.User.Roles, roles.Role{ - RoleType: roles.RoleTypeTIO, - }) + + officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{ + { + Model: transportationOffice, + LinkOnly: true, + Type: &factory.TransportationOffices.CloseoutOffice, + }, + }, []roles.RoleType{roles.RoleTypeTIO}) + return officeUser } paymentRequest := models.PaymentRequest{ @@ -250,7 +258,6 @@ func (suite *HandlerSuite) TestUpdatePaymentRequestStatusHandler() { } statusUpdater := paymentrequest.NewPaymentRequestStatusUpdater(query.NewQueryBuilder()) - suite.Run("successful status update of payment request", func() { officeUser := setupTestData() pendingPaymentRequest := factory.BuildPaymentRequest(suite.DB(), nil, nil) diff --git a/pkg/handlers/primeapi/payloads/model_to_payload_test.go b/pkg/handlers/primeapi/payloads/model_to_payload_test.go index e56a57c85ba..2626c7e119c 100644 --- a/pkg/handlers/primeapi/payloads/model_to_payload_test.go +++ b/pkg/handlers/primeapi/payloads/model_to_payload_test.go @@ -456,6 +456,20 @@ func (suite *PayloadsSuite) TestInternalServerError() { suite.Equal(traceID.String(), detailError.Instance.String()) } +func (suite *PayloadsSuite) TestNotImplementedError() { + traceID, _ := uuid.NewV4() + detail := "Err" + + noDetailError := NotImplementedError(nil, traceID) + suite.Equal(handlers.NotImplementedErrMessage, *noDetailError.Title) + suite.Equal(traceID.String(), noDetailError.Instance.String()) + + detailError := NotImplementedError(&detail, traceID) + suite.Equal(handlers.NotImplementedErrMessage, *detailError.Title) + suite.Equal(detail, *detailError.Detail) + suite.Equal(traceID.String(), detailError.Instance.String()) +} + func (suite *PayloadsSuite) TestGetDimension() { dimensionType := models.DimensionTypeItem dimensions := models.MTOServiceItemDimensions{ @@ -512,6 +526,80 @@ func (suite *PayloadsSuite) TestPaymentRequests() { suite.Equal(len(paymentRequests), len(*result)) } +func (suite *PayloadsSuite) TestMTOShipmentWithoutServiceItems() { + // Create the addresses + pickupAddress := factory.BuildAddress(suite.DB(), nil, nil) + destinationAddress := factory.BuildAddress(suite.DB(), nil, nil) + destinationType := models.DestinationTypeHomeOfRecord + secondaryPickupAddress := factory.BuildAddress(suite.DB(), nil, []factory.Trait{factory.GetTraitAddress2}) + secondaryDeliveryAddress := factory.BuildAddress(suite.DB(), nil, []factory.Trait{factory.GetTraitAddress4}) + dlhTestWeight := unit.Pound(4000) + + // Create the MTOShipment with populated PickupAddress and DestinationAddress + mtoShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + PickupAddressID: &pickupAddress.ID, + DestinationAddressID: &destinationAddress.ID, + DestinationType: &destinationType, + SecondaryPickupAddressID: &secondaryPickupAddress.ID, + SecondaryDeliveryAddressID: &secondaryDeliveryAddress.ID, + PrimeEstimatedWeight: models.PoundPointer(unit.Pound(980)), + PrimeActualWeight: &dlhTestWeight, + NTSRecordedWeight: models.PoundPointer(unit.Pound(249)), + }, + }, + }, nil) + shipmentWithoutServiceItem := MTOShipmentWithoutServiceItems(&mtoShipment) + suite.NotNil(shipmentWithoutServiceItem) +} + +func (suite *PayloadsSuite) TestMTOShipmentsWithoutServiceItems() { + // Create the addresses + pickupAddress := factory.BuildAddress(suite.DB(), nil, nil) + destinationAddress := factory.BuildAddress(suite.DB(), nil, nil) + destinationType := models.DestinationTypeHomeOfRecord + secondaryPickupAddress := factory.BuildAddress(suite.DB(), nil, []factory.Trait{factory.GetTraitAddress2}) + secondaryDeliveryAddress := factory.BuildAddress(suite.DB(), nil, []factory.Trait{factory.GetTraitAddress4}) + dlhTestWeight := unit.Pound(4000) + + // Create the MTOShipment + mtoShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + PickupAddressID: &pickupAddress.ID, + DestinationAddressID: &destinationAddress.ID, + DestinationType: &destinationType, + SecondaryPickupAddressID: &secondaryPickupAddress.ID, + SecondaryDeliveryAddressID: &secondaryDeliveryAddress.ID, + PrimeEstimatedWeight: models.PoundPointer(unit.Pound(980)), + PrimeActualWeight: &dlhTestWeight, + NTSRecordedWeight: models.PoundPointer(unit.Pound(249)), + }, + }, + }, nil) + + // Create the MTOShipment + mtoShipmentTwo := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + PickupAddressID: &pickupAddress.ID, + DestinationAddressID: &destinationAddress.ID, + DestinationType: &destinationType, + SecondaryPickupAddressID: &secondaryPickupAddress.ID, + SecondaryDeliveryAddressID: &secondaryDeliveryAddress.ID, + PrimeEstimatedWeight: models.PoundPointer(unit.Pound(980)), + PrimeActualWeight: &dlhTestWeight, + NTSRecordedWeight: models.PoundPointer(unit.Pound(249)), + }, + }, + }, nil) + shipmentList := models.MTOShipments{} + shipmentList = append(shipmentList, mtoShipmentTwo, mtoShipment) + value := MTOShipmentsWithoutServiceItems(&shipmentList) + suite.NotNil(value) +} + func (suite *PayloadsSuite) TestPaymentServiceItem() { paymentServiceItem := models.PaymentServiceItem{ ID: uuid.Must(uuid.NewV4()), @@ -605,8 +693,11 @@ func (suite *PayloadsSuite) TestShipmentAddressUpdate() { suite.Equal(strfmt.UUID(shipmentAddressUpdate.ID.String()), result.ID) } -func (suite *PayloadsSuite) TestMTOServiceItemDCRT() { +func (suite *PayloadsSuite) TestMTOServiceItemDCRTandDOFSITandDDFSIT() { reServiceCode := models.ReServiceCodeDCRT + reServiceCodeSIT := models.ReServiceCodeDOFSIT + reServiceCodeDDFSIT := models.ReServiceCodeDDFSIT + reason := "reason" dateOfContact1 := time.Now() timeMilitary1 := "1500Z" @@ -634,11 +725,83 @@ func (suite *PayloadsSuite) TestMTOServiceItemDCRT() { }, }, } + year, month, day := time.Now().Date() + aWeekAgo := time.Date(year, month, day-7, 0, 0, 0, 0, time.UTC) + departureDate := aWeekAgo.Add(time.Hour * 24 * 30) + actualPickupAddress := factory.BuildAddress(nil, nil, []factory.Trait{factory.GetTraitAddress2}) + requestApprovalRequestedStatus := false + mtoServiceItemDOFSIT := &models.MTOServiceItem{ + ID: uuid.Must(uuid.NewV4()), + ReService: models.ReService{Code: reServiceCodeSIT}, + Reason: &reason, + SITDepartureDate: &departureDate, + SITEntryDate: &aWeekAgo, + SITPostalCode: models.StringPointer("90210"), + SITOriginHHGActualAddress: &actualPickupAddress, + SITCustomerContacted: &aWeekAgo, + SITRequestedDelivery: &aWeekAgo, + SITOriginHHGOriginalAddress: &models.Address{ + StreetAddress1: "dummyStreet2", + City: "dummyCity2", + State: "FL", + PostalCode: "55555", + }, + RequestedApprovalsRequestedStatus: &requestApprovalRequestedStatus, + CustomerContacts: models.MTOServiceItemCustomerContacts{ + models.MTOServiceItemCustomerContact{ + DateOfContact: dateOfContact1, + TimeMilitary: timeMilitary1, + FirstAvailableDeliveryDate: firstAvailableDeliveryDate1, + Type: models.CustomerContactTypeFirst, + }, + models.MTOServiceItemCustomerContact{ + DateOfContact: dateOfContact2, + TimeMilitary: timeMilitary2, + FirstAvailableDeliveryDate: firstAvailableDeliveryDate2, + Type: models.CustomerContactTypeSecond, + }, + }, + } + mtoServiceItemDDFSIT := &models.MTOServiceItem{ + ID: uuid.Must(uuid.NewV4()), + ReService: models.ReService{Code: reServiceCodeDDFSIT}, + Reason: &reason, + SITDepartureDate: &departureDate, + SITEntryDate: &aWeekAgo, + SITPostalCode: models.StringPointer("90210"), + SITOriginHHGActualAddress: &actualPickupAddress, + SITCustomerContacted: &aWeekAgo, + SITRequestedDelivery: &aWeekAgo, + SITOriginHHGOriginalAddress: &models.Address{ + StreetAddress1: "dummyStreet2", + City: "dummyCity2", + State: "FL", + PostalCode: "55555", + }, + RequestedApprovalsRequestedStatus: &requestApprovalRequestedStatus, + CustomerContacts: models.MTOServiceItemCustomerContacts{ + models.MTOServiceItemCustomerContact{ + DateOfContact: dateOfContact1, + TimeMilitary: timeMilitary1, + FirstAvailableDeliveryDate: firstAvailableDeliveryDate1, + Type: models.CustomerContactTypeFirst, + }, + models.MTOServiceItemCustomerContact{ + DateOfContact: dateOfContact2, + TimeMilitary: timeMilitary2, + FirstAvailableDeliveryDate: firstAvailableDeliveryDate2, + Type: models.CustomerContactTypeSecond, + }, + }, + } resultDCRT := MTOServiceItem(mtoServiceItemDCRT) + resultDOFSIT := MTOServiceItem(mtoServiceItemDOFSIT) + resultDDFSIT := MTOServiceItem(mtoServiceItemDDFSIT) suite.NotNil(resultDCRT) - + suite.NotNil(resultDOFSIT) + suite.NotNil(resultDDFSIT) _, ok := resultDCRT.(*primemessages.MTOServiceItemDomesticCrating) suite.True(ok) diff --git a/pkg/models/move.go b/pkg/models/move.go index 55253f40b61..f0b3ac49269 100644 --- a/pkg/models/move.go +++ b/pkg/models/move.go @@ -464,6 +464,21 @@ func SaveMoveDependencies(db *pop.Connection, move *Move) (*validate.Errors, err return responseVErrors, responseError } +// FetchMoveByMoveIDWithServiceItems returns a Move along with all the associations needed to determine +// the move service item's status. +func FetchMoveByMoveIDWithServiceItems(db *pop.Connection, moveID uuid.UUID) (Move, error) { + var move Move + err := db.Q().Eager().Where("show = TRUE").Find(&move, moveID) + + if err != nil { + if errors.Cause(err).Error() == RecordNotFoundErrorString { + return Move{}, ErrFetchNotFound + } + return Move{}, err + } + return move, nil +} + // FetchMoveForMoveDates returns a Move along with all the associations needed to determine // the move dates summary information. func FetchMoveForMoveDates(db *pop.Connection, moveID uuid.UUID) (Move, error) { diff --git a/pkg/services/move_task_order/move_task_order_updater.go b/pkg/services/move_task_order/move_task_order_updater.go index 71389da1a3a..25fbec6856a 100644 --- a/pkg/services/move_task_order/move_task_order_updater.go +++ b/pkg/services/move_task_order/move_task_order_updater.go @@ -59,6 +59,9 @@ func (o moveTaskOrderUpdater) UpdateStatusServiceCounselingCompleted(appCtx appc return err } + //When submiting a move for approval - remove the SC assigned user + move.SCAssignedID = nil + // Save the move. var verrs *validate.Errors verrs, err = appCtx.DB().ValidateAndSave(move) @@ -251,6 +254,9 @@ func (o *moveTaskOrderUpdater) MakeAvailableToPrime(appCtx appcontext.AppContext return &models.Move{}, apperror.NewPreconditionFailedError(move.ID, query.StaleIdentifierError{StaleIdentifier: eTag}) } + //When approving a shipment - remove the assigned TOO user + move.TOOAssignedID = nil + // If the move is already been made available to prime, we will not need to approve and update the move, // just the provided service items. updateMove := false diff --git a/pkg/services/move_task_order/move_task_order_updater_test.go b/pkg/services/move_task_order/move_task_order_updater_test.go index ed4df5b58d5..d38acd2e8ac 100644 --- a/pkg/services/move_task_order/move_task_order_updater_test.go +++ b/pkg/services/move_task_order/move_task_order_updater_test.go @@ -12,6 +12,7 @@ import ( "github.com/transcom/mymove/pkg/etag" "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/models/roles" routemocks "github.com/transcom/mymove/pkg/route/mocks" "github.com/transcom/mymove/pkg/services" "github.com/transcom/mymove/pkg/services/ghcrateengine" @@ -29,6 +30,26 @@ func (suite *MoveTaskOrderServiceSuite) TestMoveTaskOrderUpdater_UpdateStatusSer queryBuilder := query.NewQueryBuilder() planner := &routemocks.Planner{} ppmEstimator := &mocks.PPMEstimator{} + setupTestData := func() models.OfficeUser { + + transportationOffice := factory.BuildTransportationOffice(suite.DB(), []factory.Customization{ + { + Model: models.TransportationOffice{ + ProvidesCloseout: true, + }, + }, + }, nil) + + officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{ + { + Model: transportationOffice, + LinkOnly: true, + Type: &factory.TransportationOffices.CloseoutOffice, + }, + }, []roles.RoleType{roles.RoleTypeServicesCounselor}) + + return officeUser + } setUpSignedCertificationCreatorMock := func(returnValue ...interface{}) services.SignedCertificationCreator { mockCreator := &mocks.SignedCertificationCreator{} @@ -68,6 +89,32 @@ func (suite *MoveTaskOrderServiceSuite) TestMoveTaskOrderUpdater_UpdateStatusSer moveRouter, setUpSignedCertificationCreatorMock(nil, nil), setUpSignedCertificationUpdaterMock(nil, nil), ppmEstimator, ) + suite.Run("Makes move available to Prime and Removes assigned TOO office user", func() { + session := suite.AppContextWithSessionForTest(&auth.Session{ + ApplicationName: auth.OfficeApp, + OfficeUserID: uuid.Must(uuid.NewV4()), + }) + + officeUser := setupTestData() + move := factory.BuildMoveWithShipment(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + Status: models.MoveStatusNeedsServiceCounseling, + }, + }, + { + Model: officeUser, + LinkOnly: true, + Type: &factory.OfficeUsers.SCAssignedUser, + }, + }, nil) + + eTag := etag.GenerateEtag(move.UpdatedAt) + + actualMTO, err := mtoUpdater.UpdateStatusServiceCounselingCompleted(session, move.ID, eTag) + suite.NoError(err) + suite.Nil(actualMTO.SCAssignedID) + }) suite.Run("Move status is updated successfully (with HHG shipment)", func() { session := suite.AppContextWithSessionForTest(&auth.Session{ ApplicationName: auth.OfficeApp, @@ -656,6 +703,27 @@ func (suite *MoveTaskOrderServiceSuite) TestMoveTaskOrderUpdater_ShowHide() { func (suite *MoveTaskOrderServiceSuite) TestMoveTaskOrderUpdater_MakeAvailableToPrime() { ppmEstimator := &mocks.PPMEstimator{} + setupTestData := func() models.OfficeUser { + + transportationOffice := factory.BuildTransportationOffice(suite.DB(), []factory.Customization{ + { + Model: models.TransportationOffice{ + ProvidesCloseout: true, + }, + }, + }, nil) + + officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{ + { + Model: transportationOffice, + LinkOnly: true, + Type: &factory.TransportationOffices.CloseoutOffice, + }, + }, []roles.RoleType{roles.RoleTypeTIO}) + + return officeUser + } + setUpSignedCertificationCreatorMock := func(returnValue ...interface{}) services.SignedCertificationCreator { mockCreator := &mocks.SignedCertificationCreator{} @@ -791,6 +859,32 @@ func (suite *MoveTaskOrderServiceSuite) TestMoveTaskOrderUpdater_MakeAvailableTo suite.NotNil(fetchedMove.ApprovedAt) suite.Equal(models.MoveStatusAPPROVED, fetchedMove.Status) }) + suite.Run("Makes move available to Prime and Removes assigned TOO office user", func() { + queryBuilder := query.NewQueryBuilder() + moveRouter := moverouter.NewMoveRouter() + planner := &routemocks.Planner{} + planner.On("ZipTransitDistance", + mock.AnythingOfType("*appcontext.appContext"), + mock.Anything, + mock.Anything, + ).Return(400, nil) + officeUser := setupTestData() + move := factory.BuildApprovalsRequestedMove(suite.DB(), []factory.Customization{ + { + Model: officeUser, + LinkOnly: true, + Type: &factory.OfficeUsers.TOOAssignedUser, + }, + }, nil) + + serviceItemCreator := mtoserviceitem.NewMTOServiceItemCreator(planner, queryBuilder, moveRouter, ghcrateengine.NewDomesticUnpackPricer(), ghcrateengine.NewDomesticPackPricer(), ghcrateengine.NewDomesticLinehaulPricer(), ghcrateengine.NewDomesticShorthaulPricer(), ghcrateengine.NewDomesticOriginPricer(), ghcrateengine.NewDomesticDestinationPricer(), ghcrateengine.NewFuelSurchargePricer()) + mtoUpdater := mt.NewMoveTaskOrderUpdater(queryBuilder, serviceItemCreator, moveRouter, setUpSignedCertificationCreatorMock(nil, nil), setUpSignedCertificationUpdaterMock(nil, nil), ppmEstimator) + eTag := etag.GenerateEtag(move.UpdatedAt) + updatedMove, err := mtoUpdater.MakeAvailableToPrime(suite.AppContextForTest(), move.ID, eTag, false, false) + + suite.NoError(err) + suite.Nil(updatedMove.TOOAssignedID) + }) suite.Run("Makes move available to Prime and only creates Move management when it's the only one specified", func() { queryBuilder := query.NewQueryBuilder() diff --git a/pkg/services/mto_service_item/mto_service_item_updater.go b/pkg/services/mto_service_item/mto_service_item_updater.go index 803a601dc4d..e7460944030 100644 --- a/pkg/services/mto_service_item/mto_service_item_updater.go +++ b/pkg/services/mto_service_item/mto_service_item_updater.go @@ -343,7 +343,34 @@ func (p *mtoServiceItemUpdater) approveOrRejectServiceItem( if err != nil { return err } + move := serviceItem.MoveTaskOrder + moveWithServiceItems, err := models.FetchMoveByMoveIDWithServiceItems(txnAppCtx.DB(), move.ID) + if err != nil { + return err + } + + serviceItemsNeedingReview := false + for _, request := range moveWithServiceItems.MTOServiceItems { + if request.Status == models.MTOServiceItemStatusSubmitted { + serviceItemsNeedingReview = true + break + } + } + + //remove assigned user when all service items have been reviewed + if !serviceItemsNeedingReview { + move.TOOAssignedID = nil + } + + //When updating a service item - remove the TOO assigned user + verrs, err := appCtx.DB().ValidateAndSave(&move) + if verrs != nil && verrs.HasAny() { + return apperror.NewInvalidInputError(move.ID, nil, verrs, "") + } + if err != nil { + return err + } if _, err = p.moveRouter.ApproveOrRequestApproval(txnAppCtx, move); err != nil { return err diff --git a/pkg/services/mto_service_item/mto_service_item_updater_test.go b/pkg/services/mto_service_item/mto_service_item_updater_test.go index ea5e869aa25..8fb839e8df2 100644 --- a/pkg/services/mto_service_item/mto_service_item_updater_test.go +++ b/pkg/services/mto_service_item/mto_service_item_updater_test.go @@ -21,6 +21,7 @@ import ( "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/models/roles" mocks "github.com/transcom/mymove/pkg/route/mocks" "github.com/transcom/mymove/pkg/services/address" "github.com/transcom/mymove/pkg/services/ghcrateengine" @@ -2208,6 +2209,50 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemStatus() { suite.Nil(serviceItem.RejectedAt) suite.NotNil(updatedServiceItem) }) + suite.Run("Handling assigned user When TOO reviews move and approves service item", func() { + transportationOffice := factory.BuildTransportationOffice(suite.DB(), []factory.Customization{ + { + Model: models.TransportationOffice{ + ProvidesCloseout: true, + }, + }, + }, nil) + + officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{ + { + Model: transportationOffice, + LinkOnly: true, + Type: &factory.TransportationOffices.CloseoutOffice, + }, + }, []roles.RoleType{roles.RoleTypeTOO}) + + move := factory.BuildApprovalsRequestedMove(suite.DB(), []factory.Customization{ + { + Model: officeUser, + LinkOnly: true, + Type: &factory.OfficeUsers.TOOAssignedUser, + }, + }, nil) + + serviceItem := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + }, nil) + + suite.NotNil(move.TOOAssignedUser) + eTag := etag.GenerateEtag(serviceItem.UpdatedAt) + updatedServiceItem, err := updater.ApproveOrRejectServiceItem( + suite.AppContextForTest(), serviceItem.ID, models.MTOServiceItemStatusApproved, rejectionReason, eTag) + suite.NoError(err) + err = suite.DB().Find(&move, move.ID) + suite.NoError(err) + err = suite.DB().Find(&serviceItem, serviceItem.ID) + suite.NoError(err) + suite.Nil(move.TOOAssignedID) + suite.Equal(models.MTOServiceItemStatusApproved, updatedServiceItem.Status) + }) suite.Run("When TOO approves a DDDSIT service item with an existing SITDestinationFinalAddress", func() { move := factory.BuildApprovalsRequestedMove(suite.DB(), nil, nil) diff --git a/pkg/services/payment_request/payment_request_status_updater.go b/pkg/services/payment_request/payment_request_status_updater.go index 5bc9f04c030..8ed84b5479c 100644 --- a/pkg/services/payment_request/payment_request_status_updater.go +++ b/pkg/services/payment_request/payment_request_status_updater.go @@ -8,7 +8,9 @@ import ( "github.com/transcom/mymove/pkg/appcontext" "github.com/transcom/mymove/pkg/apperror" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/models/roles" "github.com/transcom/mymove/pkg/services" + moveservice "github.com/transcom/mymove/pkg/services/move" "github.com/transcom/mymove/pkg/services/query" ) @@ -48,6 +50,9 @@ func (p *paymentRequestStatusUpdater) UpdatePaymentRequestStatus(appCtx appconte } } + paymentRequests := models.PaymentRequests{} + moveID := paymentRequest.MoveTaskOrderID + var verrs *validate.Errors var err error if eTag == "" { @@ -60,6 +65,29 @@ func (p *paymentRequestStatusUpdater) UpdatePaymentRequestStatus(appCtx appconte return nil, apperror.NewInvalidInputError(id, err, verrs, "") } + Qerr := appCtx.DB().Q().InnerJoin("moves", "payment_requests.move_id = moves.id").Where("moves.id = ?", moveID).All(&paymentRequests) + if Qerr != nil { + return nil, Qerr + } + + paymentRequestNeedingReview := false + for _, request := range paymentRequests { + if request.Status != models.PaymentRequestStatusReviewed && + request.Status != models.PaymentRequestStatusReviewedAllRejected { + paymentRequestNeedingReview = true + break + } + } + + if !paymentRequestNeedingReview { + _, err := moveservice.AssignedOfficeUserUpdater.DeleteAssignedOfficeUser(moveservice.AssignedOfficeUserUpdater{}, appCtx, moveID, roles.RoleTypeTIO) + if err != nil { + return nil, err + } + paymentRequest.MoveTaskOrder.TIOAssignedID = nil + paymentRequest.MoveTaskOrder.TIOAssignedUser = nil + } + if err != nil { switch err.(type) { case query.StaleIdentifierError: diff --git a/pkg/services/payment_request/payment_request_status_updater_test.go b/pkg/services/payment_request/payment_request_status_updater_test.go index 178f4437437..8ca36ccf5d0 100644 --- a/pkg/services/payment_request/payment_request_status_updater_test.go +++ b/pkg/services/payment_request/payment_request_status_updater_test.go @@ -7,12 +7,50 @@ import ( "github.com/transcom/mymove/pkg/etag" "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/models/roles" "github.com/transcom/mymove/pkg/services/query" "github.com/transcom/mymove/pkg/unit" ) func (suite *PaymentRequestServiceSuite) TestUpdatePaymentRequestStatus() { builder := query.NewQueryBuilder() + suite.Run("When the last payment request is updated remove the assigend user.", func() { + setupTestData := func() models.OfficeUser { + transportationOffice := factory.BuildTransportationOffice(suite.DB(), []factory.Customization{ + { + Model: models.TransportationOffice{ + ProvidesCloseout: true, + }, + }, + }, nil) + + officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{ + { + Model: transportationOffice, + LinkOnly: true, + Type: &factory.TransportationOffices.CloseoutOffice, + }, + }, []roles.RoleType{roles.RoleTypeTIO}) + + return officeUser + } + officeUser := setupTestData() + paymentRequest := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: officeUser, + LinkOnly: true, + Type: &factory.OfficeUsers.TIOAssignedUser, + }, + }, nil) + + paymentRequest.Status = models.PaymentRequestStatusReviewed + updater := NewPaymentRequestStatusUpdater(builder) + updatedPr, err := updater.UpdatePaymentRequestStatus(suite.AppContextForTest(), &paymentRequest, etag.GenerateEtag(paymentRequest.UpdatedAt)) + + suite.NoError(err) + suite.Nil(updatedPr.MoveTaskOrder.TIOAssignedID) + suite.Nil(updatedPr.MoveTaskOrder.TIOAssignedUser) + }) suite.Run("If we get a payment request pointer with a status it should update and return no error", func() { paymentRequest := factory.BuildPaymentRequest(suite.DB(), nil, nil)