Skip to content

Commit

Permalink
Merge pull request #14649 from transcom/B-21446-TIO-bulk-assignment-be
Browse files Browse the repository at this point in the history
B 21446 tio bulk assignment be
  • Loading branch information
pambecker authored Feb 5, 2025
2 parents bacd33e + 1eb0141 commit 279ea9b
Show file tree
Hide file tree
Showing 6 changed files with 717 additions and 37 deletions.
21 changes: 21 additions & 0 deletions pkg/handlers/ghcapi/queues.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,27 @@ func (h GetBulkAssignmentDataHandler) Handle(
return queues.NewGetBulkAssignmentDataInternalServerError(), err
}

officeUserData = payloads.BulkAssignmentData(appCtx, moves, officeUsers, officeUser.TransportationOffice.ID)
case string(models.QueueTypePaymentRequest):
// fetch the TIOs who work at their office
officeUsers, err := h.OfficeUserFetcherPop.FetchOfficeUsersWithWorkloadByRoleAndOffice(
appCtx,
roles.RoleTypeTIO,
officeUser.TransportationOfficeID,
)
if err != nil {
appCtx.Logger().Error("Error retreiving office users", zap.Error(err))
return queues.NewGetBulkAssignmentDataInternalServerError(), err
}
// fetch the moves available to be assigned to their office users
moves, err := h.MoveFetcherBulkAssignment.FetchMovesForBulkAssignmentPaymentRequest(
appCtx, officeUser.TransportationOffice.Gbloc, officeUser.TransportationOffice.ID,
)
if err != nil {
appCtx.Logger().Error("Error retreiving moves", zap.Error(err))
return queues.NewGetBulkAssignmentDataInternalServerError(), err
}

officeUserData = payloads.BulkAssignmentData(appCtx, moves, officeUsers, officeUser.TransportationOffice.ID)
}
return queues.NewGetBulkAssignmentDataOK().WithPayload(&officeUserData), nil
Expand Down
118 changes: 116 additions & 2 deletions pkg/handlers/ghcapi/queues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1655,7 +1655,7 @@ func (suite *HandlerSuite) TestGetServicesCounselingQueueHandler() {
}

func (suite *HandlerSuite) TestGetBulkAssignmentDataHandler() {
suite.Run("returns an unauthorized error when an attempt is made by a non supervisor", func() {
suite.Run("SC - returns an unauthorized error when an attempt is made by a non supervisor", func() {
officeUser := factory.BuildOfficeUserWithPrivileges(suite.DB(), []factory.Customization{
{
Model: models.OfficeUser{
Expand Down Expand Up @@ -1689,7 +1689,7 @@ func (suite *HandlerSuite) TestGetBulkAssignmentDataHandler() {
suite.IsNotErrResponse(response)
suite.IsType(&queues.GetBulkAssignmentDataUnauthorized{}, response)
})
suite.Run("returns properly formatted bulk assignment data", func() {
suite.Run("SC - returns properly formatted bulk assignment data", func() {
transportationOffice := factory.BuildTransportationOffice(suite.DB(), nil, nil)

officeUser := factory.BuildOfficeUserWithPrivileges(suite.DB(), []factory.Customization{
Expand Down Expand Up @@ -1892,4 +1892,118 @@ func (suite *HandlerSuite) TestGetBulkAssignmentDataHandler() {
suite.Len(payload.AvailableOfficeUsers, 1)
suite.Len(payload.BulkAssignmentMoveIDs, 1)
})

suite.Run("TIO - returns an unauthorized error when an attempt is made by a non supervisor", func() {
officeUser := factory.BuildOfficeUserWithPrivileges(suite.DB(), []factory.Customization{
{
Model: models.OfficeUser{
Email: "[email protected]",
},
},
{
Model: models.User{
Roles: []roles.Role{
{
RoleType: roles.RoleTypeTIO,
},
},
},
},
}, nil)

request := httptest.NewRequest("GET", "/queues/bulk-assignment", nil)
request = suite.AuthenticateOfficeRequest(request, officeUser)
params := queues.GetBulkAssignmentDataParams{
HTTPRequest: request,
QueueType: models.StringPointer("PAYMENT_REQUEST"),
}
handlerConfig := suite.HandlerConfig()
handler := GetBulkAssignmentDataHandler{
handlerConfig,
officeusercreator.NewOfficeUserFetcherPop(),
movefetcher.NewMoveFetcherBulkAssignment(),
}
response := handler.Handle(params)
suite.IsNotErrResponse(response)
suite.IsType(&queues.GetBulkAssignmentDataUnauthorized{}, response)
})
suite.Run("TIO - returns properly formatted bulk assignment data", func() {
transportationOffice := factory.BuildTransportationOffice(suite.DB(), nil, nil)

officeUser := factory.BuildOfficeUserWithPrivileges(suite.DB(), []factory.Customization{
{
Model: models.OfficeUser{
Email: "[email protected]",
Active: true,
},
},
{
Model: transportationOffice,
LinkOnly: true,
Type: &factory.TransportationOffices.CounselingOffice,
},
{
Model: models.User{
Privileges: []models.Privilege{
{
PrivilegeType: models.PrivilegeTypeSupervisor,
},
},
Roles: []roles.Role{
{
RoleType: roles.RoleTypeTIO,
},
},
},
},
}, nil)

// payment request to appear in the return
move := factory.BuildMoveWithShipment(suite.DB(), []factory.Customization{
{
Model: models.Move{
Status: models.MoveStatusAPPROVALSREQUESTED,
},
},
{
Model: transportationOffice,
LinkOnly: true,
Type: &factory.TransportationOffices.CounselingOffice,
},
}, nil)
factory.BuildPaymentRequest(suite.DB(), []factory.Customization{
{
Model: models.PaymentRequest{
ID: uuid.Must(uuid.NewV4()),
IsFinal: false,
Status: models.PaymentRequestStatusPending,
RejectionReason: nil,
},
},
{
Model: move,
LinkOnly: true,
},
}, nil)

request := httptest.NewRequest("GET", "/queues/bulk-assignment", nil)
request = suite.AuthenticateOfficeRequest(request, officeUser)
params := queues.GetBulkAssignmentDataParams{
HTTPRequest: request,
QueueType: models.StringPointer("PAYMENT_REQUEST"),
}
handlerConfig := suite.HandlerConfig()
handler := GetBulkAssignmentDataHandler{
handlerConfig,
officeusercreator.NewOfficeUserFetcherPop(),
movefetcher.NewMoveFetcherBulkAssignment(),
}
response := handler.Handle(params)
suite.IsNotErrResponse(response)
suite.IsType(&queues.GetBulkAssignmentDataOK{}, response)
payload := response.(*queues.GetBulkAssignmentDataOK).Payload
suite.NoError(payload.Validate(strfmt.Default))
suite.Len(payload.AvailableOfficeUsers, 1)
suite.Len(payload.BulkAssignmentMoveIDs, 1)
})
}
5 changes: 5 additions & 0 deletions pkg/models/payment_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ type PaymentRequest struct {
TPPSPaidInvoiceReports TPPSPaidInvoiceReportEntrys `has_many:"tpps_paid_invoice_reports" fk_id:"payment_request_number"`
}

type PaymentRequestWithEarliestRequestedDate struct {
ID uuid.UUID `json:"id" db:"id"`
EarliestRequestedDate time.Time `db:"requested_at"`
}

// TableName overrides the table name used by Pop.
func (p PaymentRequest) TableName() string {
return "payment_requests"
Expand Down
1 change: 1 addition & 0 deletions pkg/services/move.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type MoveFetcherBulkAssignment interface {
FetchMovesForBulkAssignmentCounseling(appCtx appcontext.AppContext, gbloc string, officeId uuid.UUID) ([]models.MoveWithEarliestDate, error)
FetchMovesForBulkAssignmentCloseout(appCtx appcontext.AppContext, gbloc string, officeId uuid.UUID) ([]models.MoveWithEarliestDate, error)
FetchMovesForBulkAssignmentTaskOrder(appCtx appcontext.AppContext, gbloc string, officeId uuid.UUID) ([]models.MoveWithEarliestDate, error)
FetchMovesForBulkAssignmentPaymentRequest(appCtx appcontext.AppContext, gbloc string, officeId uuid.UUID) ([]models.MoveWithEarliestDate, error)
}

//go:generate mockery --name MoveSearcher
Expand Down
120 changes: 88 additions & 32 deletions pkg/services/move/move_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ func (f moveFetcherBulkAssignment) FetchMovesForBulkAssignmentCounseling(appCtx
INNER JOIN mto_shipments ON mto_shipments.move_id = moves.id
LEFT JOIN ppm_shipments ON ppm_shipments.shipment_id = mto_shipments.id
WHERE
moves.status = 'NEEDS SERVICE COUNSELING'
mto_shipments.deleted_at IS NULL
AND moves.status = 'NEEDS SERVICE COUNSELING'
AND orders.gbloc = $1
AND moves.show = $2
AND moves.sc_assigned_id IS NULL
Expand Down Expand Up @@ -223,37 +224,92 @@ func (f moveFetcherBulkAssignment) FetchMovesForBulkAssignmentCloseout(appCtx ap
func (f moveFetcherBulkAssignment) FetchMovesForBulkAssignmentTaskOrder(appCtx appcontext.AppContext, gbloc string, officeId uuid.UUID) ([]models.MoveWithEarliestDate, error) {
var moves []models.MoveWithEarliestDate

err := appCtx.DB().
RawQuery(`SELECT
moves.id,
MIN(LEAST(
COALESCE(mto_shipments.requested_pickup_date, '9999-12-31'),
COALESCE(mto_shipments.requested_delivery_date, '9999-12-31'),
COALESCE(ppm_shipments.expected_departure_date, '9999-12-31')
)) AS earliest_date
FROM moves
INNER JOIN orders ON orders.id = moves.orders_id
INNER JOIN service_members ON orders.service_member_id = service_members.id
INNER JOIN mto_shipments ON mto_shipments.move_id = moves.id
LEFT JOIN ppm_shipments ON ppm_shipments.shipment_id = mto_shipments.id
LEFT JOIN move_to_gbloc ON move_to_gbloc.move_id = moves.id
WHERE
(moves.status IN ('APPROVALS REQUESTED', 'SUBMITTED', 'SERVICE COUNSELING COMPLETED'))
AND moves.show = $1
AND moves.too_assigned_id IS NULL
AND (orders.orders_type NOT IN ($2, $3, $4))
AND service_members.affiliation != 'MARINES'
AND ((mto_shipments.shipment_type != $5 AND move_to_gbloc.gbloc = $6) OR (mto_shipments.shipment_type = $7 AND orders.gbloc = $8))
GROUP BY moves.id
ORDER BY earliest_date ASC`,
models.BoolPointer(true),
internalmessages.OrdersTypeBLUEBARK,
internalmessages.OrdersTypeWOUNDEDWARRIOR,
internalmessages.OrdersTypeSAFETY,
models.MTOShipmentTypeHHGOutOfNTS,
gbloc,
models.MTOShipmentTypeHHGOutOfNTS,
gbloc).
sqlQuery := `
SELECT
moves.id,
MIN(LEAST(
COALESCE(mto_shipments.requested_pickup_date, '9999-12-31'),
COALESCE(mto_shipments.requested_delivery_date, '9999-12-31'),
COALESCE(ppm_shipments.expected_departure_date, '9999-12-31')
)) AS earliest_date
FROM moves
INNER JOIN orders ON orders.id = moves.orders_id
INNER JOIN service_members ON orders.service_member_id = service_members.id
INNER JOIN mto_shipments ON mto_shipments.move_id = moves.id
INNER JOIN duty_locations as origin_dl ON orders.origin_duty_location_id = origin_dl.id
LEFT JOIN ppm_shipments ON ppm_shipments.shipment_id = mto_shipments.id
LEFT JOIN move_to_gbloc ON move_to_gbloc.move_id = moves.id
WHERE
mto_shipments.deleted_at IS NULL
AND (moves.status IN ('APPROVALS REQUESTED', 'SUBMITTED', 'SERVICE COUNSELING COMPLETED'))
AND moves.show = $1
AND moves.too_assigned_id IS NULL
AND (orders.orders_type NOT IN ($2, $3, $4))
AND (moves.ppm_type IS NULL OR (moves.ppm_type = 'PARTIAL' or (moves.ppm_type = 'FULL' and origin_dl.provides_services_counseling = 'false'))) `
if gbloc == "USMC" {
sqlQuery += `
AND service_members.affiliation ILIKE 'MARINES' `
} else {
sqlQuery += `
AND service_members.affiliation != 'MARINES'
AND ((mto_shipments.shipment_type != 'HHG_OUTOF_NTS' AND move_to_gbloc.gbloc = '` + gbloc + `')
OR (mto_shipments.shipment_type = 'HHG_OUTOF_NTS' AND orders.gbloc = '` + gbloc + `')) `
}
sqlQuery += `
GROUP BY moves.id
ORDER BY earliest_date ASC`

err := appCtx.DB().RawQuery(sqlQuery,
models.BoolPointer(true),
internalmessages.OrdersTypeBLUEBARK,
internalmessages.OrdersTypeWOUNDEDWARRIOR,
internalmessages.OrdersTypeSAFETY).
All(&moves)

if err != nil {
return nil, fmt.Errorf("error fetching moves for office: %s with error %w", officeId, err)
}

if len(moves) < 1 {
return nil, nil
}

return moves, nil
}

func (f moveFetcherBulkAssignment) FetchMovesForBulkAssignmentPaymentRequest(appCtx appcontext.AppContext, gbloc string, officeId uuid.UUID) ([]models.MoveWithEarliestDate, error) {
var moves []models.MoveWithEarliestDate

sqlQuery := `
SELECT
moves.id,
min(payment_requests.requested_at) AS earliest_date
FROM moves
INNER JOIN orders ON orders.id = moves.orders_id
INNER JOIN service_members ON orders.service_member_id = service_members.id
INNER JOIN payment_requests on payment_requests.move_id = moves.id
LEFT JOIN move_to_gbloc ON move_to_gbloc.move_id = moves.id
WHERE moves.show = $1
AND (orders.orders_type NOT IN ($2, $3, $4))
AND moves.tio_assigned_id IS NULL
AND payment_requests.status = 'PENDING' `
if gbloc == "USMC" {
sqlQuery += `
AND service_members.affiliation ILIKE 'MARINES' `
} else {
sqlQuery += `
AND service_members.affiliation != 'MARINES'
AND move_to_gbloc.gbloc = '` + gbloc + `' `
}
sqlQuery += `
GROUP BY moves.id
ORDER BY earliest_date ASC`

err := appCtx.DB().RawQuery(sqlQuery,
models.BoolPointer(true),
internalmessages.OrdersTypeBLUEBARK,
internalmessages.OrdersTypeWOUNDEDWARRIOR,
internalmessages.OrdersTypeSAFETY).
All(&moves)

if err != nil {
Expand Down
Loading

0 comments on commit 279ea9b

Please sign in to comment.