Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

B 21446 tio bulk assignment be #14649

Merged
merged 15 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading