From ff83d5940b2dd3977ec81d032040566a7a227cf8 Mon Sep 17 00:00:00 2001 From: Paul Stonebraker Date: Tue, 28 Jan 2025 08:00:29 -0700 Subject: [PATCH 1/5] bug fixes for assignment --- pkg/gen/ghcapi/embedded_spec.go | 36 ++++++++++ .../queues/get_moves_queue_parameters.go | 27 +++++++ .../queues/get_moves_queue_urlbuilder.go | 9 +++ .../get_payment_requests_queue_parameters.go | 27 +++++++ .../get_payment_requests_queue_urlbuilder.go | 9 +++ ...et_services_counseling_queue_parameters.go | 27 +++++++ ...et_services_counseling_queue_urlbuilder.go | 9 +++ .../internal/payloads/model_to_payload.go | 70 +++++++++++-------- .../payloads/model_to_payload_test.go | 11 +-- pkg/handlers/ghcapi/queues.go | 21 +++++- src/components/Table/TableQueue.jsx | 1 + src/hooks/queries.js | 12 ++-- .../HeadquartersQueues/HeadquartersQueues.jsx | 6 +- src/pages/Office/MoveQueue/MoveQueue.jsx | 3 +- .../PaymentRequestQueue.jsx | 3 +- .../ServicesCounselingQueue.jsx | 3 + src/pages/Office/index.jsx | 20 ++++-- src/services/ghcApi.js | 42 +++++++++-- swagger-def/ghc.yaml | 12 ++++ swagger/ghc.yaml | 12 ++++ 20 files changed, 304 insertions(+), 56 deletions(-) diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go index 3db3ec66e71..66ce57b95a4 100644 --- a/pkg/gen/ghcapi/embedded_spec.go +++ b/pkg/gen/ghcapi/embedded_spec.go @@ -4617,6 +4617,12 @@ func init() { "description": "Used to illustrate which user is assigned to this payment request.\n", "name": "assignedTo", "in": "query" + }, + { + "type": "string", + "description": "user's actively logged in role", + "name": "activeRole", + "in": "query" } ], "responses": { @@ -4820,6 +4826,12 @@ func init() { "description": "filters using a counselingOffice name of the move", "name": "counselingOffice", "in": "query" + }, + { + "type": "string", + "description": "user's actively logged in role", + "name": "activeRole", + "in": "query" } ], "responses": { @@ -4974,6 +4986,12 @@ func init() { "description": "Used to return a queue for a GBLOC other than the default of the current user. Requires the HQ role or a secondary transportation office assignment. The parameter is ignored if the requesting user does not have the necessary role or assignment.\n", "name": "viewAsGBLOC", "in": "query" + }, + { + "type": "string", + "description": "user's actively logged in role", + "name": "activeRole", + "in": "query" } ], "responses": { @@ -21265,6 +21283,12 @@ func init() { "description": "Used to illustrate which user is assigned to this payment request.\n", "name": "assignedTo", "in": "query" + }, + { + "type": "string", + "description": "user's actively logged in role", + "name": "activeRole", + "in": "query" } ], "responses": { @@ -21480,6 +21504,12 @@ func init() { "description": "filters using a counselingOffice name of the move", "name": "counselingOffice", "in": "query" + }, + { + "type": "string", + "description": "user's actively logged in role", + "name": "activeRole", + "in": "query" } ], "responses": { @@ -21640,6 +21670,12 @@ func init() { "description": "Used to return a queue for a GBLOC other than the default of the current user. Requires the HQ role or a secondary transportation office assignment. The parameter is ignored if the requesting user does not have the necessary role or assignment.\n", "name": "viewAsGBLOC", "in": "query" + }, + { + "type": "string", + "description": "user's actively logged in role", + "name": "activeRole", + "in": "query" } ], "responses": { diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_moves_queue_parameters.go b/pkg/gen/ghcapi/ghcoperations/queues/get_moves_queue_parameters.go index f1cfe32cfae..2123cc801d9 100644 --- a/pkg/gen/ghcapi/ghcoperations/queues/get_moves_queue_parameters.go +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_moves_queue_parameters.go @@ -34,6 +34,10 @@ type GetMovesQueueParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` + /*user's actively logged in role + In: query + */ + ActiveRole *string /* In: query */ @@ -124,6 +128,11 @@ func (o *GetMovesQueueParams) BindRequest(r *http.Request, route *middleware.Mat qs := runtime.Values(r.URL.Query()) + qActiveRole, qhkActiveRole, _ := qs.GetOK("activeRole") + if err := o.bindActiveRole(qActiveRole, qhkActiveRole, route.Formats); err != nil { + res = append(res, err) + } + qAppearedInTooAt, qhkAppearedInTooAt, _ := qs.GetOK("appearedInTooAt") if err := o.bindAppearedInTooAt(qAppearedInTooAt, qhkAppearedInTooAt, route.Formats); err != nil { res = append(res, err) @@ -219,6 +228,24 @@ func (o *GetMovesQueueParams) BindRequest(r *http.Request, route *middleware.Mat return nil } +// bindActiveRole binds and validates parameter ActiveRole from query. +func (o *GetMovesQueueParams) bindActiveRole(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + return nil + } + o.ActiveRole = &raw + + return nil +} + // bindAppearedInTooAt binds and validates parameter AppearedInTooAt from query. func (o *GetMovesQueueParams) bindAppearedInTooAt(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_moves_queue_urlbuilder.go b/pkg/gen/ghcapi/ghcoperations/queues/get_moves_queue_urlbuilder.go index ec05a50a3eb..7d809005df8 100644 --- a/pkg/gen/ghcapi/ghcoperations/queues/get_moves_queue_urlbuilder.go +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_moves_queue_urlbuilder.go @@ -16,6 +16,7 @@ import ( // GetMovesQueueURL generates an URL for the get moves queue operation type GetMovesQueueURL struct { + ActiveRole *string AppearedInTooAt *strfmt.DateTime AssignedTo *string Branch *string @@ -69,6 +70,14 @@ func (o *GetMovesQueueURL) Build() (*url.URL, error) { qs := make(url.Values) + var activeRoleQ string + if o.ActiveRole != nil { + activeRoleQ = *o.ActiveRole + } + if activeRoleQ != "" { + qs.Set("activeRole", activeRoleQ) + } + var appearedInTooAtQ string if o.AppearedInTooAt != nil { appearedInTooAtQ = o.AppearedInTooAt.String() diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_payment_requests_queue_parameters.go b/pkg/gen/ghcapi/ghcoperations/queues/get_payment_requests_queue_parameters.go index fe0d201031e..423c3f7eaa6 100644 --- a/pkg/gen/ghcapi/ghcoperations/queues/get_payment_requests_queue_parameters.go +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_payment_requests_queue_parameters.go @@ -34,6 +34,10 @@ type GetPaymentRequestsQueueParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` + /*user's actively logged in role + In: query + */ + ActiveRole *string /*Used to illustrate which user is assigned to this payment request. In: query @@ -118,6 +122,11 @@ func (o *GetPaymentRequestsQueueParams) BindRequest(r *http.Request, route *midd qs := runtime.Values(r.URL.Query()) + qActiveRole, qhkActiveRole, _ := qs.GetOK("activeRole") + if err := o.bindActiveRole(qActiveRole, qhkActiveRole, route.Formats); err != nil { + res = append(res, err) + } + qAssignedTo, qhkAssignedTo, _ := qs.GetOK("assignedTo") if err := o.bindAssignedTo(qAssignedTo, qhkAssignedTo, route.Formats); err != nil { res = append(res, err) @@ -208,6 +217,24 @@ func (o *GetPaymentRequestsQueueParams) BindRequest(r *http.Request, route *midd return nil } +// bindActiveRole binds and validates parameter ActiveRole from query. +func (o *GetPaymentRequestsQueueParams) bindActiveRole(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + return nil + } + o.ActiveRole = &raw + + return nil +} + // bindAssignedTo binds and validates parameter AssignedTo from query. func (o *GetPaymentRequestsQueueParams) bindAssignedTo(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_payment_requests_queue_urlbuilder.go b/pkg/gen/ghcapi/ghcoperations/queues/get_payment_requests_queue_urlbuilder.go index 1b5aa0e8b3b..45ac4629c59 100644 --- a/pkg/gen/ghcapi/ghcoperations/queues/get_payment_requests_queue_urlbuilder.go +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_payment_requests_queue_urlbuilder.go @@ -16,6 +16,7 @@ import ( // GetPaymentRequestsQueueURL generates an URL for the get payment requests queue operation type GetPaymentRequestsQueueURL struct { + ActiveRole *string AssignedTo *string Branch *string CounselingOffice *string @@ -68,6 +69,14 @@ func (o *GetPaymentRequestsQueueURL) Build() (*url.URL, error) { qs := make(url.Values) + var activeRoleQ string + if o.ActiveRole != nil { + activeRoleQ = *o.ActiveRole + } + if activeRoleQ != "" { + qs.Set("activeRole", activeRoleQ) + } + var assignedToQ string if o.AssignedTo != nil { assignedToQ = *o.AssignedTo diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_services_counseling_queue_parameters.go b/pkg/gen/ghcapi/ghcoperations/queues/get_services_counseling_queue_parameters.go index 2b03f53918f..3f596c1f60c 100644 --- a/pkg/gen/ghcapi/ghcoperations/queues/get_services_counseling_queue_parameters.go +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_services_counseling_queue_parameters.go @@ -34,6 +34,10 @@ type GetServicesCounselingQueueParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` + /*user's actively logged in role + In: query + */ + ActiveRole *string /*Used to illustrate which user is assigned to this payment request. In: query @@ -148,6 +152,11 @@ func (o *GetServicesCounselingQueueParams) BindRequest(r *http.Request, route *m qs := runtime.Values(r.URL.Query()) + qActiveRole, qhkActiveRole, _ := qs.GetOK("activeRole") + if err := o.bindActiveRole(qActiveRole, qhkActiveRole, route.Formats); err != nil { + res = append(res, err) + } + qAssignedTo, qhkAssignedTo, _ := qs.GetOK("assignedTo") if err := o.bindAssignedTo(qAssignedTo, qhkAssignedTo, route.Formats); err != nil { res = append(res, err) @@ -273,6 +282,24 @@ func (o *GetServicesCounselingQueueParams) BindRequest(r *http.Request, route *m return nil } +// bindActiveRole binds and validates parameter ActiveRole from query. +func (o *GetServicesCounselingQueueParams) bindActiveRole(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + return nil + } + o.ActiveRole = &raw + + return nil +} + // bindAssignedTo binds and validates parameter AssignedTo from query. func (o *GetServicesCounselingQueueParams) bindAssignedTo(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_services_counseling_queue_urlbuilder.go b/pkg/gen/ghcapi/ghcoperations/queues/get_services_counseling_queue_urlbuilder.go index d7ad7668c07..b013d4b0089 100644 --- a/pkg/gen/ghcapi/ghcoperations/queues/get_services_counseling_queue_urlbuilder.go +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_services_counseling_queue_urlbuilder.go @@ -16,6 +16,7 @@ import ( // GetServicesCounselingQueueURL generates an URL for the get services counseling queue operation type GetServicesCounselingQueueURL struct { + ActiveRole *string AssignedTo *string Branch *string CloseoutInitiated *strfmt.DateTime @@ -75,6 +76,14 @@ func (o *GetServicesCounselingQueueURL) Build() (*url.URL, error) { qs := make(url.Values) + var activeRoleQ string + if o.ActiveRole != nil { + activeRoleQ = *o.ActiveRole + } + if activeRoleQ != "" { + qs.Set("activeRole", activeRoleQ) + } + var assignedToQ string if o.AssignedTo != nil { assignedToQ = *o.AssignedTo diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index f97ea101fc0..09ae933d70b 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -2181,12 +2181,12 @@ func BulkAssignmentData(appCtx appcontext.AppContext, moves []models.MoveWithEar return *bulkAssignmentData } -func queueMoveIsAssignable(move models.Move, assignedToUser *ghcmessages.AssignedOfficeUser, isCloseoutQueue bool, officeUser models.OfficeUser, ppmCloseoutGblocs bool) bool { +func queueMoveIsAssignable(move models.Move, assignedToUser *ghcmessages.AssignedOfficeUser, isCloseoutQueue bool, officeUser models.OfficeUser, ppmCloseoutGblocs bool, activeRole string) bool { // default to false isAssignable := false // HQ role is read only - if officeUser.User.Roles.HasRole(roles.RoleTypeHQ) { + if activeRole == string(roles.RoleTypeHQ) { isAssignable = false return isAssignable } @@ -2198,13 +2198,13 @@ func queueMoveIsAssignable(move models.Move, assignedToUser *ghcmessages.Assigne isSupervisor := officeUser.User.Privileges.HasPrivilege(models.PrivilegeTypeSupervisor) // in TOO queues, all moves are assignable for supervisor users - if officeUser.User.Roles.HasRole(roles.RoleTypeTOO) && isSupervisor { + if activeRole == string(roles.RoleTypeTOO) && isSupervisor { isAssignable = true } // if it is assigned in the SCs queue // it is only assignable if the user is a supervisor... - if officeUser.User.Roles.HasRole(roles.RoleTypeServicesCounselor) && isSupervisor { + if activeRole == string(roles.RoleTypeServicesCounselor) && isSupervisor { // AND we are in the counseling queue AND the move's counseling office is the supervisor's transportation office if !isCloseoutQueue && move.CounselingOfficeID != nil && *move.CounselingOfficeID == officeUser.TransportationOfficeID { isAssignable = true @@ -2224,35 +2224,36 @@ func queueMoveIsAssignable(move models.Move, assignedToUser *ghcmessages.Assigne } func servicesCounselorAvailableOfficeUsers(move models.Move, officeUsers []models.OfficeUser, officeUser models.OfficeUser, ppmCloseoutGblocs bool, isCloseoutQueue bool) []models.OfficeUser { - if officeUser.User.Roles.HasRole(roles.RoleTypeServicesCounselor) { - // if the office user currently assigned to the move works outside of the logged in users counseling office - // add them to the set - if move.SCAssignedUser != nil && move.SCAssignedUser.TransportationOfficeID != officeUser.TransportationOfficeID { - officeUsers = append(officeUsers, *move.SCAssignedUser) - } + // if the office user currently assigned to the move works outside of the logged in users counseling office + // add them to the set + if move.SCAssignedUser != nil && move.SCAssignedUser.TransportationOfficeID != officeUser.TransportationOfficeID { + officeUsers = append(officeUsers, *move.SCAssignedUser) + } - // if there is no counseling office - // OR if our current user doesn't work at the move's counseling office - // only available user should be themself - if !isCloseoutQueue && (move.CounselingOfficeID == nil) || (move.CounselingOfficeID != nil && *move.CounselingOfficeID != officeUser.TransportationOfficeID) { - officeUsers = models.OfficeUsers{officeUser} - } + var onlySelfAssign bool - // if its the closeout queue and its not a Navy, Marine, or Coast Guard user - // and the move doesn't have a closeout office - // OR the move's closeout office is not the office users office - // only available user should be themself - if isCloseoutQueue && !ppmCloseoutGblocs && move.CloseoutOfficeID == nil || (move.CloseoutOfficeID != nil && *move.CloseoutOfficeID != officeUser.TransportationOfficeID) { - officeUsers = models.OfficeUsers{officeUser} + // if there is no counseling office + // OR if our current user doesn't work at the move's counseling office + // only available user should be themself + onlySelfAssign = (move.CounselingOfficeID == nil) || (move.CounselingOfficeID != nil && *move.CounselingOfficeID != officeUser.TransportationOfficeID) + if !isCloseoutQueue && onlySelfAssign { + officeUsers = models.OfficeUsers{officeUser} + } - } + // if its the closeout queue and its not a Navy, Marine, or Coast Guard user + // and the move doesn't have a closeout office + // OR the move's closeout office is not the office users office + // only available user should be themself + onlySelfAssign = (move.CloseoutOfficeID == nil) || (move.CloseoutOfficeID != nil && *move.CloseoutOfficeID != officeUser.TransportationOfficeID) + if isCloseoutQueue && !ppmCloseoutGblocs && onlySelfAssign { + officeUsers = models.OfficeUsers{officeUser} } return officeUsers } // QueueMoves payload -func QueueMoves(moves []models.Move, officeUsers []models.OfficeUser, requestedPpmStatus *models.PPMShipmentStatus, officeUser models.OfficeUser, officeUsersSafety []models.OfficeUser) *ghcmessages.QueueMoves { +func QueueMoves(moves []models.Move, officeUsers []models.OfficeUser, requestedPpmStatus *models.PPMShipmentStatus, officeUser models.OfficeUser, officeUsersSafety []models.OfficeUser, activeRole string) *ghcmessages.QueueMoves { queueMoves := make(ghcmessages.QueueMoves, len(moves)) for i, move := range moves { customer := move.Orders.ServiceMember @@ -2324,10 +2325,10 @@ func QueueMoves(moves []models.Move, officeUsers []models.OfficeUser, requestedP // determine if there is an assigned user var assignedToUser *ghcmessages.AssignedOfficeUser - if officeUser.User.Roles.HasRole(roles.RoleTypeServicesCounselor) && move.SCAssignedUser != nil { + if activeRole == string(roles.RoleTypeServicesCounselor) && move.SCAssignedUser != nil { assignedToUser = AssignedOfficeUser(move.SCAssignedUser) } - if officeUser.User.Roles.HasRole(roles.RoleTypeTOO) && move.TOOAssignedUser != nil { + if activeRole == string(roles.RoleTypeTOO) && move.TOOAssignedUser != nil { assignedToUser = AssignedOfficeUser(move.TOOAssignedUser) } @@ -2336,7 +2337,7 @@ func QueueMoves(moves []models.Move, officeUsers []models.OfficeUser, requestedP // requestedPpmStatus also represents if we are viewing the closeout queue isCloseoutQueue := requestedPpmStatus != nil && *requestedPpmStatus == models.PPMShipmentStatusNeedsCloseout // determine if the move is assignable - assignable := queueMoveIsAssignable(move, assignedToUser, isCloseoutQueue, officeUser, ppmCloseoutGblocs) + assignable := queueMoveIsAssignable(move, assignedToUser, isCloseoutQueue, officeUser, ppmCloseoutGblocs, activeRole) isSupervisor := officeUser.User.Privileges.HasPrivilege(models.PrivilegeTypeSupervisor) // only need to attach available office users if move is assignable @@ -2348,7 +2349,16 @@ func QueueMoves(moves []models.Move, officeUsers []models.OfficeUser, requestedP if isSupervisor && move.Orders.OrdersType == "SAFETY" { availableOfficeUsers = officeUsersSafety } - if officeUser.User.Roles.HasRole(roles.RoleTypeServicesCounselor) { + + // if the assigned user does not work at the logged in users transportation office + // append them to the office users + if activeRole == string(roles.RoleTypeTOO) { + if move.TOOAssignedUser != nil && move.TOOAssignedUser.TransportationOfficeID != officeUser.TransportationOfficeID { + availableOfficeUsers = append(availableOfficeUsers, *move.TOOAssignedUser) + } + } + + if activeRole == string(roles.RoleTypeServicesCounselor) { availableOfficeUsers = servicesCounselorAvailableOfficeUsers(move, availableOfficeUsers, officeUser, ppmCloseoutGblocs, isCloseoutQueue) } @@ -2449,7 +2459,7 @@ func queuePaymentRequestStatus(paymentRequest models.PaymentRequest) string { } // QueuePaymentRequests payload -func QueuePaymentRequests(paymentRequests *models.PaymentRequests, officeUsers []models.OfficeUser, officeUser models.OfficeUser, officeUsersSafety []models.OfficeUser) *ghcmessages.QueuePaymentRequests { +func QueuePaymentRequests(paymentRequests *models.PaymentRequests, officeUsers []models.OfficeUser, officeUser models.OfficeUser, officeUsersSafety []models.OfficeUser, activeRole string) *ghcmessages.QueuePaymentRequests { queuePaymentRequests := make(ghcmessages.QueuePaymentRequests, len(*paymentRequests)) @@ -2494,7 +2504,7 @@ func QueuePaymentRequests(paymentRequests *models.PaymentRequests, officeUsers [ isAssignable = true } - if officeUser.User.Roles.HasRole(roles.RoleTypeHQ) { + if activeRole == string(roles.RoleTypeHQ) { isAssignable = false } 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 657f980e910..1557803e06d 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go @@ -195,7 +195,8 @@ func (suite *PayloadsSuite) TestPaymentRequestQueue() { var officeUsers models.OfficeUsers var officeUsersSafety models.OfficeUsers officeUsers = append(officeUsers, officeUser) - var paymentRequestsQueue = QueuePaymentRequests(&paymentRequests, officeUsers, officeUser, officeUsersSafety) + activeRole := string(roles.RoleTypeTIO) + var paymentRequestsQueue = QueuePaymentRequests(&paymentRequests, officeUsers, officeUser, officeUsersSafety, activeRole) suite.Run("Test Payment request is assignable due to not being assigend", func() { paymentRequestCopy := *paymentRequestsQueue @@ -214,7 +215,7 @@ func (suite *PayloadsSuite) TestPaymentRequestQueue() { paymentRequests[0].MoveTaskOrder.TIOAssignedUser = &officeUserTIO paymentRequests[0].MoveTaskOrder.CounselingOffice = &transportationOffice - paymentRequestsQueue = QueuePaymentRequests(&paymentRequests, officeUsers, officeUser, officeUsersSafety) + paymentRequestsQueue = QueuePaymentRequests(&paymentRequests, officeUsers, officeUser, officeUsersSafety, activeRole) suite.Run("Test PaymentRequest has both Counseling Office and TIO AssignedUser ", func() { PaymentRequestsCopy := *paymentRequestsQueue @@ -228,14 +229,14 @@ func (suite *PayloadsSuite) TestPaymentRequestQueue() { }) suite.Run("Test PaymentRequest is assignable due to user Supervisor role", func() { - paymentRequests := QueuePaymentRequests(&paymentRequests, officeUsers, officeUser, officeUsersSafety) + paymentRequests := QueuePaymentRequests(&paymentRequests, officeUsers, officeUser, officeUsersSafety, activeRole) paymentRequestCopy := *paymentRequests suite.Equal(paymentRequestCopy[0].Assignable, true) }) - officeUserHQ := factory.BuildOfficeUserWithRoles(suite.DB(), nil, []roles.RoleType{roles.RoleTypeHQ}) + activeRole = string(roles.RoleTypeHQ) suite.Run("Test PaymentRequest is not assignable due to user HQ role", func() { - paymentRequests := QueuePaymentRequests(&paymentRequests, officeUsers, officeUserHQ, officeUsersSafety) + paymentRequests := QueuePaymentRequests(&paymentRequests, officeUsers, officeUser, officeUsersSafety, activeRole) paymentRequestCopy := *paymentRequests suite.Equal(paymentRequestCopy[0].Assignable, false) }) diff --git a/pkg/handlers/ghcapi/queues.go b/pkg/handlers/ghcapi/queues.go index ed4980df650..03f4129aa05 100644 --- a/pkg/handlers/ghcapi/queues.go +++ b/pkg/handlers/ghcapi/queues.go @@ -64,6 +64,11 @@ func (h GetMovesQueueHandler) Handle(params queues.GetMovesQueueParams) middlewa CounselingOffice: params.CounselingOffice, } + var activeRole string + if params.ActiveRole != nil { + activeRole = *params.ActiveRole + } + // When no status filter applied, TOO should only see moves with status of New Move, Service Counseling Completed, or Approvals Requested if params.Status == nil { ListOrderParams.Status = []string{string(models.MoveStatusServiceCounselingCompleted), string(models.MoveStatusAPPROVALSREQUESTED), string(models.MoveStatusSUBMITTED)} @@ -165,7 +170,7 @@ func (h GetMovesQueueHandler) Handle(params queues.GetMovesQueueParams) middlewa } } - queueMoves := payloads.QueueMoves(moves, officeUsers, nil, officeUser, officeUsersSafety) + queueMoves := payloads.QueueMoves(moves, officeUsers, nil, officeUser, officeUsersSafety, activeRole) result := &ghcmessages.QueueMovesResult{ Page: *ListOrderParams.Page, @@ -271,6 +276,11 @@ func (h GetPaymentRequestsQueueHandler) Handle( CounselingOffice: params.CounselingOffice, } + var activeRole string + if params.ActiveRole != nil { + activeRole = *params.ActiveRole + } + listPaymentRequestParams.Status = []string{string(models.QueuePaymentRequestPaymentRequested)} // Let's set default values for page and perPage if we don't get arguments for them. We'll use 1 for page and 20 @@ -368,7 +378,7 @@ func (h GetPaymentRequestsQueueHandler) Handle( } } - queuePaymentRequests := payloads.QueuePaymentRequests(paymentRequests, officeUsers, officeUser, officeUsersSafety) + queuePaymentRequests := payloads.QueuePaymentRequests(paymentRequests, officeUsers, officeUser, officeUsersSafety, activeRole) result := &ghcmessages.QueuePaymentRequestsResult{ TotalCount: int64(count), @@ -429,6 +439,11 @@ func (h GetServicesCounselingQueueHandler) Handle( SCAssignedUser: params.AssignedTo, } + var activeRole string + if params.ActiveRole != nil { + activeRole = *params.ActiveRole + } + var requestedPpmStatus models.PPMShipmentStatus if params.NeedsPPMCloseout != nil && *params.NeedsPPMCloseout { requestedPpmStatus = models.PPMShipmentStatusNeedsCloseout @@ -538,7 +553,7 @@ func (h GetServicesCounselingQueueHandler) Handle( } } - queueMoves := payloads.QueueMoves(moves, officeUsers, &requestedPpmStatus, officeUser, officeUsersSafety) + queueMoves := payloads.QueueMoves(moves, officeUsers, &requestedPpmStatus, officeUser, officeUsersSafety, activeRole) result := &ghcmessages.QueueMovesResult{ Page: *ListOrderParams.Page, diff --git a/src/components/Table/TableQueue.jsx b/src/components/Table/TableQueue.jsx index d657df5007f..32e13cfc75f 100644 --- a/src/components/Table/TableQueue.jsx +++ b/src/components/Table/TableQueue.jsx @@ -125,6 +125,7 @@ const TableQueue = ({ currentPage, currentPageSize, viewAsGBLOC: selectedGbloc, + activeRole, }); // react-table setup below diff --git a/src/hooks/queries.js b/src/hooks/queries.js index ce469702359..17ccfe30f5a 100644 --- a/src/hooks/queries.js +++ b/src/hooks/queries.js @@ -571,9 +571,10 @@ export const useMovesQueueQueries = ({ currentPage = PAGINATION_PAGE_DEFAULT, currentPageSize = PAGINATION_PAGE_SIZE_DEFAULT, viewAsGBLOC, + activeRole, }) => { const { data = {}, ...movesQueueQuery } = useQuery( - [MOVES_QUEUE, { sort, order, filters, currentPage, currentPageSize, viewAsGBLOC }], + [MOVES_QUEUE, { sort, order, filters, currentPage, currentPageSize, viewAsGBLOC, activeRole }], ({ queryKey }) => getMovesQueue(...queryKey), ); const { isLoading, isError, isSuccess } = movesQueueQuery; @@ -593,11 +594,12 @@ export const useServicesCounselingQueuePPMQueries = ({ currentPage = PAGINATION_PAGE_DEFAULT, currentPageSize = PAGINATION_PAGE_SIZE_DEFAULT, viewAsGBLOC, + activeRole, }) => { const { data = {}, ...servicesCounselingQueueQuery } = useQuery( [ SERVICES_COUNSELING_QUEUE, - { sort, order, filters, currentPage, currentPageSize, needsPPMCloseout: true, viewAsGBLOC }, + { sort, order, filters, currentPage, currentPageSize, needsPPMCloseout: true, viewAsGBLOC, activeRole }, ], ({ queryKey }) => getServicesCounselingPPMQueue(...queryKey), ); @@ -619,11 +621,12 @@ export const useServicesCounselingQueueQueries = ({ currentPage = PAGINATION_PAGE_DEFAULT, currentPageSize = PAGINATION_PAGE_SIZE_DEFAULT, viewAsGBLOC, + activeRole, }) => { const { data = {}, ...servicesCounselingQueueQuery } = useQuery( [ SERVICES_COUNSELING_QUEUE, - { sort, order, filters, currentPage, currentPageSize, needsPPMCloseout: false, viewAsGBLOC }, + { sort, order, filters, currentPage, currentPageSize, needsPPMCloseout: false, viewAsGBLOC, activeRole }, ], ({ queryKey }) => getServicesCounselingQueue(...queryKey), ); @@ -645,9 +648,10 @@ export const usePaymentRequestQueueQueries = ({ currentPage = PAGINATION_PAGE_DEFAULT, currentPageSize = PAGINATION_PAGE_SIZE_DEFAULT, viewAsGBLOC, + activeRole, }) => { const { data = {}, ...paymentRequestsQueueQuery } = useQuery( - [PAYMENT_REQUESTS_QUEUE, { sort, order, filters, currentPage, currentPageSize, viewAsGBLOC }], + [PAYMENT_REQUESTS_QUEUE, { sort, order, filters, currentPage, currentPageSize, viewAsGBLOC, activeRole }], ({ queryKey }) => getPaymentRequestsQueue(...queryKey), ); diff --git a/src/pages/Office/HeadquartersQueues/HeadquartersQueues.jsx b/src/pages/Office/HeadquartersQueues/HeadquartersQueues.jsx index 1786b4525ff..c9d08481f3b 100644 --- a/src/pages/Office/HeadquartersQueues/HeadquartersQueues.jsx +++ b/src/pages/Office/HeadquartersQueues/HeadquartersQueues.jsx @@ -38,7 +38,7 @@ import { milmoveLogger } from 'utils/milmoveLog'; import ConnectedFlashMessage from 'containers/FlashMessage/FlashMessage'; import CustomerSearchForm from 'components/CustomerSearchForm/CustomerSearchForm'; -const HeadquartersQueue = ({ isQueueManagementFFEnabled }) => { +const HeadquartersQueue = ({ isQueueManagementFFEnabled, activeRole }) => { const navigate = useNavigate(); const { queueType } = useParams(); const [search, setSearch] = useState({ moveCode: null, dodID: null, customerName: null, paymentRequestCode: null }); @@ -247,6 +247,7 @@ const HeadquartersQueue = ({ isQueueManagementFFEnabled }) => { csvExportQueueFetcher={getMovesQueue} csvExportQueueFetcherKey="queueMoves" sessionStorageKey={queueType} + activeRole={activeRole} /> ); @@ -273,6 +274,7 @@ const HeadquartersQueue = ({ isQueueManagementFFEnabled }) => { csvExportQueueFetcher={getPaymentRequestsQueue} csvExportQueueFetcherKey="queuePaymentRequests" sessionStorageKey={queueType} + activeRole={activeRole} /> ); @@ -299,6 +301,7 @@ const HeadquartersQueue = ({ isQueueManagementFFEnabled }) => { csvExportQueueFetcher={getServicesCounselingPPMQueue} csvExportQueueFetcherKey="queueMoves" sessionStorageKey={queueType} + activeRole={activeRole} /> ); @@ -326,6 +329,7 @@ const HeadquartersQueue = ({ isQueueManagementFFEnabled }) => { csvExportQueueFetcher={getServicesCounselingQueue} csvExportQueueFetcherKey="queueMoves" sessionStorageKey={queueType} + activeRole={activeRole} /> ); diff --git a/src/pages/Office/MoveQueue/MoveQueue.jsx b/src/pages/Office/MoveQueue/MoveQueue.jsx index 95b703189fc..12d42ac5d22 100644 --- a/src/pages/Office/MoveQueue/MoveQueue.jsx +++ b/src/pages/Office/MoveQueue/MoveQueue.jsx @@ -188,7 +188,7 @@ export const columns = (moveLockFlag, isQueueManagementEnabled, showBranchFilter return cols; }; -const MoveQueue = ({ isQueueManagementFFEnabled, userPrivileges, isBulkAssignmentFFEnabled }) => { +const MoveQueue = ({ isQueueManagementFFEnabled, userPrivileges, isBulkAssignmentFFEnabled, activeRole }) => { const navigate = useNavigate(); const { queueType } = useParams(); const [search, setSearch] = useState({ moveCode: null, dodID: null, customerName: null, paymentRequestCode: null }); @@ -334,6 +334,7 @@ const MoveQueue = ({ isQueueManagementFFEnabled, userPrivileges, isBulkAssignmen key={queueType} isSupervisor={supervisor} isBulkAssignmentFFEnabled={isBulkAssignmentFFEnabled} + activeRole={activeRole} /> ); diff --git a/src/pages/Office/PaymentRequestQueue/PaymentRequestQueue.jsx b/src/pages/Office/PaymentRequestQueue/PaymentRequestQueue.jsx index a46518a2627..c43cf7bfbe6 100644 --- a/src/pages/Office/PaymentRequestQueue/PaymentRequestQueue.jsx +++ b/src/pages/Office/PaymentRequestQueue/PaymentRequestQueue.jsx @@ -191,7 +191,7 @@ export const columns = (moveLockFlag, isQueueManagementEnabled, showBranchFilter return cols; }; -const PaymentRequestQueue = ({ isQueueManagementFFEnabled, userPrivileges, isBulkAssignmentFFEnabled }) => { +const PaymentRequestQueue = ({ isQueueManagementFFEnabled, userPrivileges, isBulkAssignmentFFEnabled, activeRole }) => { const { queueType } = useParams(); const navigate = useNavigate(); const [search, setSearch] = useState({ moveCode: null, dodID: null, customerName: null, paymentRequestCode: null }); @@ -334,6 +334,7 @@ const PaymentRequestQueue = ({ isQueueManagementFFEnabled, userPrivileges, isBul key={queueType} isSupervisor={supervisor} isBulkAssignmentFFEnabled={isBulkAssignmentFFEnabled} + activeRole={activeRole} /> ); diff --git a/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx b/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx index 44b11a5567d..5ba55412a60 100644 --- a/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx +++ b/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx @@ -433,6 +433,7 @@ const ServicesCounselingQueue = ({ isQueueManagementFFEnabled, officeUser, isBulkAssignmentFFEnabled, + activeRole, }) => { const { queueType } = useParams(); const { data, isLoading, isError } = useUserQueries(); @@ -670,6 +671,7 @@ const ServicesCounselingQueue = ({ key={queueType} isSupervisor={supervisor} isBulkAssignmentFFEnabled={isBulkAssignmentFFEnabled} + activeRole={activeRole} /> ); @@ -699,6 +701,7 @@ const ServicesCounselingQueue = ({ key={queueType} isSupervisor={supervisor} isBulkAssignmentFFEnabled={isBulkAssignmentFFEnabled} + activeRole={activeRole} /> ); diff --git a/src/pages/Office/index.jsx b/src/pages/Office/index.jsx index 8a945c4c7f8..69f55fbf90c 100644 --- a/src/pages/Office/index.jsx +++ b/src/pages/Office/index.jsx @@ -292,7 +292,7 @@ export class OfficeApp extends Component { end element={ - + } /> @@ -301,7 +301,10 @@ export class OfficeApp extends Component { path="/invoicing/queue" element={ - + } /> @@ -311,7 +314,10 @@ export class OfficeApp extends Component { end element={ - + } /> @@ -336,6 +342,7 @@ export class OfficeApp extends Component { userPrivileges={userPrivileges} isQueueManagementFFEnabled={queueManagementFlag} isBulkAssignmentFFEnabled={bulkAssignmentFlag} + activeRole={activeRole} /> } @@ -367,6 +374,7 @@ export class OfficeApp extends Component { isQueueManagementFFEnabled={queueManagementFlag} userPrivileges={userPrivileges} isBulkAssignmentFFEnabled={bulkAssignmentFlag} + activeRole={activeRole} /> } @@ -382,6 +390,7 @@ export class OfficeApp extends Component { isQueueManagementFFEnabled={queueManagementFlag} userPrivileges={userPrivileges} isBulkAssignmentFFEnabled={bulkAssignmentFlag} + activeRole={activeRole} /> } @@ -394,7 +403,10 @@ export class OfficeApp extends Component { end element={ - + } /> diff --git a/src/services/ghcApi.js b/src/services/ghcApi.js index 1b787b7b8ad..a6c14a65e19 100644 --- a/src/services/ghcApi.js +++ b/src/services/ghcApi.js @@ -618,7 +618,7 @@ export function deleteShipment({ shipmentID, normalize = false, schemaKey = 'shi export async function getMovesQueue( key, - { sort, order, filters = [], currentPage = 1, currentPageSize = 20, viewAsGBLOC }, + { sort, order, filters = [], currentPage = 1, currentPageSize = 20, viewAsGBLOC, activeRole }, ) { const operationPath = 'queues.getMovesQueue'; const paramFilters = {}; @@ -627,14 +627,23 @@ export async function getMovesQueue( }); return makeGHCRequest( operationPath, - { sort, order, page: currentPage, perPage: currentPageSize, viewAsGBLOC, ...paramFilters }, + { sort, order, page: currentPage, perPage: currentPageSize, viewAsGBLOC, activeRole, ...paramFilters }, { schemaKey: 'queueMovesResult', normalize: false }, ); } export async function getServicesCounselingQueue( key, - { sort, order, filters = [], currentPage = 1, currentPageSize = 20, needsPPMCloseout = false, viewAsGBLOC }, + { + sort, + order, + filters = [], + currentPage = 1, + currentPageSize = 20, + needsPPMCloseout = false, + viewAsGBLOC, + activeRole, + }, ) { const operationPath = 'queues.getServicesCounselingQueue'; const paramFilters = {}; @@ -651,6 +660,7 @@ export async function getServicesCounselingQueue( perPage: currentPageSize, needsPPMCloseout, viewAsGBLOC, + activeRole, ...paramFilters, }, @@ -674,7 +684,16 @@ export async function getServicesCounselingOriginLocations(needsPPMCloseout, vie export async function getServicesCounselingPPMQueue( key, - { sort, order, filters = [], currentPage = 1, currentPageSize = 20, needsPPMCloseout = true, viewAsGBLOC }, + { + sort, + order, + filters = [], + currentPage = 1, + currentPageSize = 20, + needsPPMCloseout = true, + viewAsGBLOC, + activeRole, + }, ) { const operationPath = 'queues.getServicesCounselingQueue'; const paramFilters = {}; @@ -684,14 +703,23 @@ export async function getServicesCounselingPPMQueue( return makeGHCRequest( operationPath, - { sort, order, page: currentPage, perPage: currentPageSize, needsPPMCloseout, viewAsGBLOC, ...paramFilters }, + { + sort, + order, + page: currentPage, + perPage: currentPageSize, + needsPPMCloseout, + viewAsGBLOC, + ...paramFilters, + activeRole, + }, { schemaKey: 'queueMovesResult', normalize: false }, ); } export async function getPaymentRequestsQueue( key, - { sort, order, filters = [], currentPage = 1, currentPageSize = 20, viewAsGBLOC }, + { sort, order, filters = [], currentPage = 1, currentPageSize = 20, viewAsGBLOC, activeRole }, ) { const operationPath = 'queues.getPaymentRequestsQueue'; const paramFilters = {}; @@ -700,7 +728,7 @@ export async function getPaymentRequestsQueue( }); return makeGHCRequest( operationPath, - { sort, order, page: currentPage, perPage: currentPageSize, viewAsGBLOC, ...paramFilters }, + { sort, order, page: currentPage, perPage: currentPageSize, viewAsGBLOC, activeRole, ...paramFilters }, { schemaKey: 'queuePaymentRequestsResult', normalize: false }, ); } diff --git a/swagger-def/ghc.yaml b/swagger-def/ghc.yaml index df06a4ca220..2ecf88e122d 100644 --- a/swagger-def/ghc.yaml +++ b/swagger-def/ghc.yaml @@ -3504,6 +3504,10 @@ paths: type: string description: | Used to illustrate which user is assigned to this payment request. + - in: query + name: activeRole + type: string + description: user's actively logged in role responses: '200': description: Successfully returned all moves matching the criteria @@ -3726,6 +3730,10 @@ paths: name: counselingOffice type: string description: filters using a counselingOffice name of the move + - in: query + name: activeRole + type: string + description: user's actively logged in role responses: '200': description: Successfully returned all moves matching the criteria @@ -3822,6 +3830,10 @@ paths: type: string description: | Used to return a queue for a GBLOC other than the default of the current user. Requires the HQ role or a secondary transportation office assignment. The parameter is ignored if the requesting user does not have the necessary role or assignment. + - in: query + name: activeRole + type: string + description: user's actively logged in role responses: '200': description: Successfully returned all moves matching the criteria diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml index a92ed3016a6..8f47f21efc0 100644 --- a/swagger/ghc.yaml +++ b/swagger/ghc.yaml @@ -3637,6 +3637,10 @@ paths: type: string description: | Used to illustrate which user is assigned to this payment request. + - in: query + name: activeRole + type: string + description: user's actively logged in role responses: '200': description: Successfully returned all moves matching the criteria @@ -3892,6 +3896,10 @@ paths: name: counselingOffice type: string description: filters using a counselingOffice name of the move + - in: query + name: activeRole + type: string + description: user's actively logged in role responses: '200': description: Successfully returned all moves matching the criteria @@ -4008,6 +4016,10 @@ paths: current user. Requires the HQ role or a secondary transportation office assignment. The parameter is ignored if the requesting user does not have the necessary role or assignment. + - in: query + name: activeRole + type: string + description: user's actively logged in role responses: '200': description: Successfully returned all moves matching the criteria From 8ff11345a7061f3ffdd016b6ff7f1cde867089f8 Mon Sep 17 00:00:00 2001 From: Paul Stonebraker Date: Wed, 29 Jan 2025 14:49:54 +0000 Subject: [PATCH 2/5] bugfix for TIO assigned --- pkg/handlers/ghcapi/internal/payloads/model_to_payload.go | 8 ++++++++ .../payment_request/payment_request_list_fetcher.go | 1 + 2 files changed, 9 insertions(+) diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index 09ae933d70b..366c2d9e120 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -2458,6 +2458,7 @@ func queuePaymentRequestStatus(paymentRequest models.PaymentRequest) string { } +// dad // QueuePaymentRequests payload func QueuePaymentRequests(paymentRequests *models.PaymentRequests, officeUsers []models.OfficeUser, officeUser models.OfficeUser, officeUsersSafety []models.OfficeUser, activeRole string) *ghcmessages.QueuePaymentRequests { @@ -2516,6 +2517,13 @@ func QueuePaymentRequests(paymentRequests *models.PaymentRequests, officeUsers [ if isSupervisor && orders.OrdersType == "SAFETY" { availableOfficeUsers = officeUsersSafety } + + // if the assigned TIO doesn't work at the user's counseling office, append them + if queuePaymentRequests[i].AssignedTo != nil && paymentRequest.MoveTaskOrder.TIOAssignedUser.TransportationOfficeID != officeUser.TransportationOfficeID { + availableOfficeUsers = append(availableOfficeUsers, *paymentRequest.MoveTaskOrder.TIOAssignedUser) + } + + // if they're not a supervisor and it is assignable, the only option should be themself if !isSupervisor { availableOfficeUsers = models.OfficeUsers{officeUser} } diff --git a/pkg/services/payment_request/payment_request_list_fetcher.go b/pkg/services/payment_request/payment_request_list_fetcher.go index 11f0c52805c..fc42b14dc63 100644 --- a/pkg/services/payment_request/payment_request_list_fetcher.go +++ b/pkg/services/payment_request/payment_request_list_fetcher.go @@ -65,6 +65,7 @@ func (f *paymentRequestListFetcher) FetchPaymentRequestList(appCtx appcontext.Ap "MoveTaskOrder.Orders.OriginDutyLocation.TransportationOffice", "MoveTaskOrder.Orders.OriginDutyLocation.Address", "MoveTaskOrder.TIOAssignedUser", + "MoveTaskOrder.TIOAssignedUser.TransportationOfficeID", "MoveTaskOrder.CounselingOffice", // See note further below about having to do this in a separate Load call due to a Pop issue. // "MoveTaskOrder.Orders.ServiceMember", From 57dd6914e8cef83f77d0044252f99f37ad55284a Mon Sep 17 00:00:00 2001 From: Paul Stonebraker Date: Thu, 30 Jan 2025 07:16:50 +0000 Subject: [PATCH 3/5] add tests for supervisors available office users for assignment --- pkg/handlers/ghcapi/queues_test.go | 242 +++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) diff --git a/pkg/handlers/ghcapi/queues_test.go b/pkg/handlers/ghcapi/queues_test.go index f1eb8a3c106..fdec7399152 100644 --- a/pkg/handlers/ghcapi/queues_test.go +++ b/pkg/handlers/ghcapi/queues_test.go @@ -1821,3 +1821,245 @@ func (suite *HandlerSuite) TestGetBulkAssignmentDataHandler() { suite.Len(payload.BulkAssignmentMoveIDs, 1) }) } + +type availableOfficeUserSubtestData struct { + officeUsers []models.OfficeUser + office models.TransportationOffice +} + +func (suite *HandlerSuite) TestAvailableOfficeUsers() { + setupOfficeUserData := func(role1 roles.RoleType, role2 roles.RoleType) availableOfficeUserSubtestData { + subtestData := &availableOfficeUserSubtestData{} + transportationOffice := factory.BuildTransportationOffice(suite.DB(), nil, nil) + + // lets generate a few office users + // these first two are what we want returned in the query + // office user 1 is the supervisor making the request + officeUser1 := factory.BuildOfficeUserWithPrivileges(suite.DB(), []factory.Customization{ + { + Model: models.OfficeUser{ + LastName: "Aname", + Email: "officeuser1@example.com", + Active: true, + }, + }, + { + Model: transportationOffice, + LinkOnly: true, + Type: &factory.TransportationOffices.CounselingOffice, + }, + { + Model: models.User{ + Privileges: []models.Privilege{ + { + PrivilegeType: models.PrivilegeTypeSupervisor, + }, + }, + Roles: []roles.Role{ + { + RoleType: role1, + }, + }, + }, + }, + }, nil) + + // officeUser2 is their underling + officeUser2 := factory.BuildOfficeUserWithPrivileges(suite.DB(), []factory.Customization{ + { + Model: models.OfficeUser{ + LastName: "Bname", + Email: "officeuser2@example.com", + Active: true, + }, + }, + { + Model: transportationOffice, + LinkOnly: true, + Type: &factory.TransportationOffices.CounselingOffice, + }, + { + Model: models.User{ + Roles: []roles.Role{ + { + RoleType: role1, + }, + }, + }, + }, + }, nil) + + // this office user shares their role but does NOT work at their office so should not be returned + factory.BuildOfficeUserWithPrivileges(suite.DB(), []factory.Customization{ + { + Model: models.OfficeUser{ + Email: "officeuser3@example.com", + Active: true, + }, + }, + { + Model: models.User{ + Roles: []roles.Role{ + { + RoleType: role1, + }, + }, + }, + }, + }, nil) + + // this office users works at their office, but doesn't share the same role, and should not be returned + factory.BuildOfficeUserWithPrivileges(suite.DB(), []factory.Customization{ + { + Model: models.OfficeUser{ + Email: "officeuser4@example.com", + Active: true, + }, + }, + { + Model: transportationOffice, + LinkOnly: true, + Type: &factory.TransportationOffices.CounselingOffice, + }, + { + Model: models.User{ + Roles: []roles.Role{ + { + RoleType: role2, + }, + }, + }, + }, + }, nil) + + availableOfficeUsers := []models.OfficeUser{officeUser1, officeUser2} + subtestData.officeUsers = availableOfficeUsers + subtestData.office = transportationOffice + return *subtestData + } + suite.Run("properly fetches a TOO supervisor's available office users for assignment", func() { + subtestData := setupOfficeUserData(roles.RoleTypeTOO, roles.RoleTypeServicesCounselor) + waf := entitlements.NewWeightAllotmentFetcher() + + hhgMove := factory.BuildSubmittedMove(suite.DB(), nil, nil) + + factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: hhgMove, + LinkOnly: true, + }, + { + Model: models.MTOShipment{ + Status: models.MTOShipmentStatusSubmitted, + }, + }, + }, nil) + + request := httptest.NewRequest("GET", "/queues/moves", nil) + request = suite.AuthenticateOfficeRequest(request, subtestData.officeUsers[0]) + params := queues.GetMovesQueueParams{ + HTTPRequest: request, + } + handlerConfig := suite.HandlerConfig() + mockUnlocker := movelocker.NewMoveUnlocker() + handler := GetMovesQueueHandler{ + handlerConfig, + order.NewOrderFetcher(waf), + mockUnlocker, + officeusercreator.NewOfficeUserFetcherPop(), + } + + response := handler.Handle(params) + suite.IsNotErrResponse(response) + suite.IsType(&queues.GetMovesQueueOK{}, response) + payload := response.(*queues.GetMovesQueueOK).Payload + + suite.NotNil(payload.QueueMoves) + suite.NotNil(payload.QueueMoves[0].AvailableOfficeUsers) + suite.Equal(2, len(payload.QueueMoves[0].AvailableOfficeUsers)) + suite.Equal(subtestData.officeUsers[0].ID.String(), payload.QueueMoves[0].AvailableOfficeUsers[0].OfficeUserID.String()) + suite.Equal(subtestData.officeUsers[1].ID.String(), payload.QueueMoves[0].AvailableOfficeUsers[1].OfficeUserID.String()) + }) + suite.Run("properly fetches a SC supervisor's available office users for assignment", func() { + subtestData := setupOfficeUserData(roles.RoleTypeServicesCounselor, roles.RoleTypeTOO) + waf := entitlements.NewWeightAllotmentFetcher() + + needsCounselingMove := factory.BuildNeedsServiceCounselingMove(suite.DB(), []factory.Customization{ + { + Model: subtestData.office, + LinkOnly: true, + Type: &factory.TransportationOffices.CounselingOffice, + }, + }, nil) + + factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: needsCounselingMove, + LinkOnly: true, + }, + }, nil) + + request := httptest.NewRequest("GET", "/queues/counseling", nil) + request = suite.AuthenticateOfficeRequest(request, subtestData.officeUsers[0]) + params := queues.GetServicesCounselingQueueParams{ + HTTPRequest: request, + } + handlerConfig := suite.HandlerConfig() + mockUnlocker := movelocker.NewMoveUnlocker() + handler := GetServicesCounselingQueueHandler{ + handlerConfig, + order.NewOrderFetcher(waf), + mockUnlocker, + officeusercreator.NewOfficeUserFetcherPop(), + } + + response := handler.Handle(params) + suite.IsNotErrResponse(response) + suite.IsType(&queues.GetServicesCounselingQueueOK{}, response) + payload := response.(*queues.GetServicesCounselingQueueOK).Payload + + suite.NotNil(payload.QueueMoves) + suite.NotNil(payload.QueueMoves[0].AvailableOfficeUsers) + suite.Equal(2, len(payload.QueueMoves[0].AvailableOfficeUsers)) + suite.Equal(subtestData.officeUsers[0].ID.String(), payload.QueueMoves[0].AvailableOfficeUsers[0].OfficeUserID.String()) + suite.Equal(subtestData.officeUsers[1].ID.String(), payload.QueueMoves[0].AvailableOfficeUsers[1].OfficeUserID.String()) + }) + + suite.Run("properly fetches a TIO supervisor's available office users for assignment", func() { + subtestData := setupOfficeUserData(roles.RoleTypeTIO, roles.RoleTypeTOO) + hhgMove := factory.BuildMoveWithShipment(suite.DB(), nil, nil) + + factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: hhgMove, + LinkOnly: true, + }, + }, nil) + + request := httptest.NewRequest("GET", "/queues/payment-requests", nil) + request = suite.AuthenticateOfficeRequest(request, subtestData.officeUsers[0]) + params := queues.GetPaymentRequestsQueueParams{ + HTTPRequest: request, + } + handlerConfig := suite.HandlerConfig() + mockUnlocker := movelocker.NewMoveUnlocker() + handler := GetPaymentRequestsQueueHandler{ + handlerConfig, + paymentrequest.NewPaymentRequestListFetcher(), + mockUnlocker, + officeusercreator.NewOfficeUserFetcherPop(), + } + + response := handler.Handle(params) + suite.IsNotErrResponse(response) + suite.IsType(&queues.GetPaymentRequestsQueueOK{}, response) + payload := response.(*queues.GetPaymentRequestsQueueOK).Payload + + suite.NotNil(payload.QueuePaymentRequests) + suite.NotNil(payload.QueuePaymentRequests[0].AvailableOfficeUsers) + suite.Equal(2, len(payload.QueuePaymentRequests[0].AvailableOfficeUsers)) + suite.Equal(subtestData.officeUsers[0].ID.String(), payload.QueuePaymentRequests[0].AvailableOfficeUsers[0].OfficeUserID.String()) + suite.Equal(subtestData.officeUsers[1].ID.String(), payload.QueuePaymentRequests[0].AvailableOfficeUsers[1].OfficeUserID.String()) + }) + +} From 1d87448c49fb7dde9ae8299059edc0f050cd5993 Mon Sep 17 00:00:00 2001 From: Paul Stonebraker Date: Thu, 30 Jan 2025 13:58:24 +0000 Subject: [PATCH 4/5] bugfix for hq role not seeing any assigned data --- pkg/handlers/ghcapi/internal/payloads/model_to_payload.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index 2db144c2897..7e2ef9fc277 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -2335,10 +2335,10 @@ func QueueMoves(moves []models.Move, officeUsers []models.OfficeUser, requestedP // determine if there is an assigned user var assignedToUser *ghcmessages.AssignedOfficeUser - if activeRole == string(roles.RoleTypeServicesCounselor) && move.SCAssignedUser != nil { + if (activeRole == string(roles.RoleTypeServicesCounselor) || activeRole == string(roles.RoleTypeHQ)) && move.SCAssignedUser != nil { assignedToUser = AssignedOfficeUser(move.SCAssignedUser) } - if activeRole == string(roles.RoleTypeTOO) && move.TOOAssignedUser != nil { + if (activeRole == string(roles.RoleTypeTOO) || activeRole == string(roles.RoleTypeHQ)) && move.TOOAssignedUser != nil { assignedToUser = AssignedOfficeUser(move.TOOAssignedUser) } @@ -2468,7 +2468,6 @@ func queuePaymentRequestStatus(paymentRequest models.PaymentRequest) string { } -// dad // QueuePaymentRequests payload func QueuePaymentRequests(paymentRequests *models.PaymentRequests, officeUsers []models.OfficeUser, officeUser models.OfficeUser, officeUsersSafety []models.OfficeUser, activeRole string) *ghcmessages.QueuePaymentRequests { From ade3a6d6c007738b9eb72954252e4958d42fd62c Mon Sep 17 00:00:00 2001 From: pambecker Date: Wed, 5 Feb 2025 22:27:59 +0000 Subject: [PATCH 5/5] Add TOO and TIO user to list of office users if not already in it and they are assigned to the move; cleanup --- .../internal/payloads/model_to_payload.go | 29 ++++++++++++++----- .../office_user/office_user_fetcher.go | 4 --- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index e7f27a55145..1a97f52cded 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -2376,10 +2376,16 @@ func QueueMoves(moves []models.Move, officeUsers []models.OfficeUser, requestedP availableOfficeUsers = officeUsersSafety } - // if the assigned user does not work at the logged in users transportation office - // append them to the office users - if activeRole == string(roles.RoleTypeTOO) { - if move.TOOAssignedUser != nil && move.TOOAssignedUser.TransportationOfficeID != officeUser.TransportationOfficeID { + // if the assigned user is not in the returned list of available users append them to the end + if (activeRole == string(roles.RoleTypeTOO)) && (move.TOOAssignedUser != nil) { + userFound := false + for _, officeUser := range availableOfficeUsers { + if officeUser.ID == *move.TOOAssignedID { + userFound = true + break + } + } + if !userFound { availableOfficeUsers = append(availableOfficeUsers, *move.TOOAssignedUser) } } @@ -2543,9 +2549,18 @@ func QueuePaymentRequests(paymentRequests *models.PaymentRequests, officeUsers [ availableOfficeUsers = officeUsersSafety } - // if the assigned TIO doesn't work at the user's counseling office, append them - if queuePaymentRequests[i].AssignedTo != nil && paymentRequest.MoveTaskOrder.TIOAssignedUser.TransportationOfficeID != officeUser.TransportationOfficeID { - availableOfficeUsers = append(availableOfficeUsers, *paymentRequest.MoveTaskOrder.TIOAssignedUser) + // if the assigned user is not in the returned list of available users append them to the end + if paymentRequest.MoveTaskOrder.TIOAssignedUser != nil { + userFound := false + for _, officeUser := range availableOfficeUsers { + if officeUser.ID == paymentRequest.MoveTaskOrder.TIOAssignedUser.ID { + userFound = true + break + } + } + if !userFound { + availableOfficeUsers = append(availableOfficeUsers, *paymentRequest.MoveTaskOrder.TIOAssignedUser) + } } // if they're not a supervisor and it is assignable, the only option should be themself diff --git a/pkg/services/office_user/office_user_fetcher.go b/pkg/services/office_user/office_user_fetcher.go index 881c278ac8a..99a6448f1fe 100644 --- a/pkg/services/office_user/office_user_fetcher.go +++ b/pkg/services/office_user/office_user_fetcher.go @@ -82,8 +82,6 @@ func (o *officeUserFetcherPop) FetchOfficeUsersByRoleAndOffice(appCtx appcontext "User", "User.Roles", "User.Privileges", - "TransportationOffice", - "TransportationOffice.Gbloc", ). Join("users", "users.id = office_users.user_id"). Join("users_roles", "users.id = users_roles.user_id"). @@ -109,8 +107,6 @@ func (o *officeUserFetcherPop) FetchSafetyMoveOfficeUsersByRoleAndOffice(appCtx "User", "User.Roles", "User.Privileges", - "TransportationOffice", - "TransportationOffice.Gbloc", ). Join("users", "users.id = office_users.user_id"). Join("users_roles", "users.id = users_roles.user_id").