From 81f7b95206b5f1a2c85734627e4ad28a861756e5 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Tue, 10 Dec 2024 16:11:02 +0000 Subject: [PATCH 01/15] initial commit, using existing work from 21940 but will change this to a db func --- migrations/app/migrations_manifest.txt | 1 + ...73216_add_destination_queue_db_func.up.sql | 106 +++ pkg/gen/ghcapi/configure_mymove.go | 5 + pkg/gen/ghcapi/embedded_spec.go | 326 +++++++++ pkg/gen/ghcapi/ghcoperations/mymove_api.go | 12 + .../queues/get_destination_requests_queue.go | 58 ++ ...t_destination_requests_queue_parameters.go | 644 ++++++++++++++++++ ...et_destination_requests_queue_responses.go | 149 ++++ ...t_destination_requests_queue_urlbuilder.go | 274 ++++++++ pkg/handlers/ghcapi/api.go | 7 + pkg/handlers/ghcapi/queues.go | 126 ++++ pkg/services/mocks/OrderFetcher.go | 37 + pkg/services/order.go | 1 + pkg/services/order/order_fetcher.go | 148 +++- src/constants/routes.js | 6 +- src/hooks/queries.js | 23 + src/pages/Office/MoveQueue/MoveQueue.jsx | 44 +- src/services/ghcApi.js | 16 + swagger-def/ghc.yaml | 112 +++ swagger/ghc.yaml | 119 ++++ 20 files changed, 2209 insertions(+), 5 deletions(-) create mode 100644 migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql create mode 100644 pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue.go create mode 100644 pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_parameters.go create mode 100644 pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_responses.go create mode 100644 pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_urlbuilder.go diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index b1d24b20447..fefc26f6685 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1075,3 +1075,4 @@ 20250113201232_update_estimated_pricing_procs_add_is_peak_func.up.sql 20250116200912_disable_homesafe_stg_cert.up.sql 20250120144247_update_pricing_proc_to_use_110_percent_weight.up.sql +20250123173216_add_destination_queue_db_func.up.sql diff --git a/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql b/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql new file mode 100644 index 00000000000..8808b472e68 --- /dev/null +++ b/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql @@ -0,0 +1,106 @@ +CREATE OR REPLACE FUNCTION get_destination_queue( + move_code TEXT DEFAULT NULL, -- Search parameter for Move Code + input_move_id UUID DEFAULT NULL, -- Search parameter for Move ID + page INTEGER DEFAULT 1, -- Page number for pagination + per_page INTEGER DEFAULT 20, -- Number of results per page + branch TEXT DEFAULT NULL, -- Filter: service_member.affiliation + edipi TEXT DEFAULT NULL, -- Filter: service_member.edipi + emplid TEXT DEFAULT NULL, -- Filter: service_member.emplid + customer_name TEXT DEFAULT NULL, -- Filter: service_member.first_name + last_name + destination_duty_location TEXT DEFAULT NULL,-- Filter: orders.new_duty_location_id.name + origin_duty_location TEXT DEFAULT NULL, -- Filter: orders.origin_duty_location_id.name + origin_gbloc TEXT DEFAULT NULL, -- Filter: move.counseling_office_transportation_office.gbloc + submitted_at TIMESTAMP DEFAULT NULL, -- Filter: moves.submitted_at + appeared_in_too_at TIMESTAMP DEFAULT NULL, -- Filter: moves.appeared_in_too_at + too_assigned_user TEXT DEFAULT NULL -- Filter: moves.too_assigned_id -> office_users.first_name + last_name +) +RETURNS TABLE ( + move_id UUID, + locator TEXT, + orders_id UUID, + available_to_prime_at TIMESTAMP WITH TIME ZONE, + show BOOLEAN, + total_count BIGINT +) AS $$ +DECLARE + sql_query TEXT; + offset_value INTEGER; +BEGIN + -- OFFSET for pagination + offset_value := (page - 1) * per_page; + + sql_query := ' + SELECT moves.id AS move_id, + moves.locator::TEXT AS locator, + moves.orders_id, + moves.available_to_prime_at, + moves.show, + COUNT(*) OVER() AS total_count + FROM moves + INNER JOIN orders ON moves.orders_id = orders.id + INNER JOIN service_members ON orders.service_member_id = service_members.id + LEFT JOIN duty_locations AS new_duty_locations ON orders.new_duty_location_id = new_duty_locations.id + LEFT JOIN duty_locations AS origin_duty_locations ON orders.origin_duty_location_id = origin_duty_locations.id + LEFT JOIN office_users ON moves.too_assigned_id = office_users.id + LEFT JOIN transportation_offices AS counseling_offices + ON moves.counseling_transportation_office_id = counseling_offices.id + WHERE moves.available_to_prime_at IS NOT NULL + AND moves.show = TRUE + '; + + -- add dynamic filters based on provided parameters + IF move_code IS NOT NULL THEN + sql_query := sql_query || ' AND moves.locator ILIKE ''%' || UPPER(move_code) || '%'' '; + END IF; + + IF input_move_id IS NOT NULL THEN + sql_query := sql_query || ' AND moves.id = ''' || input_move_id || ''' '; + END IF; + + IF branch IS NOT NULL THEN + sql_query := sql_query || ' AND service_members.affiliation ILIKE ''%' || branch || '%'' '; + END IF; + + IF edipi IS NOT NULL THEN + sql_query := sql_query || ' AND service_members.edipi ILIKE ''%' || edipi || '%'' '; + END IF; + + IF emplid IS NOT NULL THEN + sql_query := sql_query || ' AND service_members.emplid ILIKE ''%' || emplid || '%'' '; + END IF; + + IF customer_name IS NOT NULL THEN + sql_query := sql_query || ' AND (service_members.first_name || '' '' || service_members.last_name) ILIKE ''%' || customer_name || '%'' '; + END IF; + + IF destination_duty_location IS NOT NULL THEN + sql_query := sql_query || ' AND new_duty_locations.name ILIKE ''%' || destination_duty_location || '%'' '; + END IF; + + IF origin_duty_location IS NOT NULL THEN + sql_query := sql_query || ' AND origin_duty_locations.name ILIKE ''%' || origin_duty_location || '%'' '; + END IF; + + IF origin_gbloc IS NOT NULL THEN + sql_query := sql_query || ' AND counseling_offices.gbloc ILIKE ''%' || origin_gbloc || '%'' '; + END IF; + + IF submitted_at IS NOT NULL THEN + sql_query := sql_query || ' AND moves.submitted_at = ''' || submitted_at || ''' '; + END IF; + + IF appeared_in_too_at IS NOT NULL THEN + sql_query := sql_query || ' AND moves.appeared_in_too_at = ''' || appeared_in_too_at || ''' '; + END IF; + + IF too_assigned_user IS NOT NULL THEN + sql_query := sql_query || ' AND (office_users.first_name || '' '' || office_users.last_name) ILIKE ''%' || too_assigned_user || '%'' '; + END IF; + + sql_query := sql_query || ' ORDER BY moves.id ASC '; + sql_query := sql_query || ' LIMIT ' || per_page || ' OFFSET ' || offset_value || ' '; + + RETURN QUERY EXECUTE sql_query; + +END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/pkg/gen/ghcapi/configure_mymove.go b/pkg/gen/ghcapi/configure_mymove.go index 32eb5174c09..08d71eb7034 100644 --- a/pkg/gen/ghcapi/configure_mymove.go +++ b/pkg/gen/ghcapi/configure_mymove.go @@ -242,6 +242,11 @@ func configureAPI(api *ghcoperations.MymoveAPI) http.Handler { return middleware.NotImplemented("operation customer_support_remarks.GetCustomerSupportRemarksForMove has not yet been implemented") }) } + if api.QueuesGetDestinationRequestsQueueHandler == nil { + api.QueuesGetDestinationRequestsQueueHandler = queues.GetDestinationRequestsQueueHandlerFunc(func(params queues.GetDestinationRequestsQueueParams) middleware.Responder { + return middleware.NotImplemented("operation queues.GetDestinationRequestsQueue has not yet been implemented") + }) + } if api.GhcDocumentsGetDocumentHandler == nil { api.GhcDocumentsGetDocumentHandler = ghc_documents.GetDocumentHandlerFunc(func(params ghc_documents.GetDocumentParams) middleware.Responder { return middleware.NotImplemented("operation ghc_documents.GetDocument has not yet been implemented") diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go index 3db3ec66e71..878032b252f 100644 --- a/pkg/gen/ghcapi/embedded_spec.go +++ b/pkg/gen/ghcapi/embedded_spec.go @@ -4676,6 +4676,166 @@ func init() { } } }, + "/queues/destination-requests": { + "get": { + "description": "A TOO will view this queue when they have destination requests tied to their GBLOC. This includes unapproved destination SIT service items (including shuttle) and destination address requests that are not approved.\n", + "produces": [ + "application/json" + ], + "tags": [ + "queues" + ], + "summary": "Gets queued list of all customer moves by GBLOC that have destination requests (destination SIT, shuttle, address requests)", + "operationId": "getDestinationRequestsQueue", + "parameters": [ + { + "type": "integer", + "description": "requested page of results", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "results per page", + "name": "perPage", + "in": "query" + }, + { + "enum": [ + "customerName", + "edipi", + "emplid", + "branch", + "locator", + "status", + "originDutyLocation", + "destinationDutyLocation", + "requestedMoveDate", + "appearedInTooAt", + "assignedTo", + "counselingOffice" + ], + "type": "string", + "description": "field that results should be sorted by", + "name": "sort", + "in": "query" + }, + { + "enum": [ + "asc", + "desc" + ], + "type": "string", + "description": "direction of sort order if applied", + "name": "order", + "in": "query" + }, + { + "type": "string", + "name": "branch", + "in": "query" + }, + { + "type": "string", + "name": "locator", + "in": "query" + }, + { + "type": "string", + "name": "customerName", + "in": "query" + }, + { + "type": "string", + "name": "edipi", + "in": "query" + }, + { + "type": "string", + "name": "emplid", + "in": "query" + }, + { + "uniqueItems": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "name": "originDutyLocation", + "in": "query" + }, + { + "type": "string", + "name": "destinationDutyLocation", + "in": "query" + }, + { + "type": "string", + "format": "date-time", + "name": "appearedInTooAt", + "in": "query" + }, + { + "type": "string", + "description": "filters the requested pickup date of a shipment on the move", + "name": "requestedMoveDate", + "in": "query" + }, + { + "uniqueItems": true, + "type": "array", + "items": { + "enum": [ + "APPROVALS REQUESTED" + ], + "type": "string" + }, + "description": "Filtering for the status.", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "order type", + "name": "orderType", + "in": "query" + }, + { + "type": "string", + "description": "Used to return a queue for a GBLOC other than the default of the current user. Requires the HQ role. The parameter is ignored if the requesting user does not have the necessary role.\n", + "name": "viewAsGBLOC", + "in": "query" + }, + { + "type": "string", + "description": "Used to illustrate which user is assigned to this move.\n", + "name": "assignedTo", + "in": "query" + }, + { + "type": "string", + "description": "filters using a counselingOffice name of the move", + "name": "counselingOffice", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successfully returned all moves matching the criteria", + "schema": { + "$ref": "#/definitions/QueueMovesResult" + } + }, + "403": { + "$ref": "#/responses/PermissionDenied" + }, + "500": { + "$ref": "#/responses/ServerError" + } + } + } + }, "/queues/moves": { "get": { "description": "An office TOO user will be assigned a transportation office that will determine which moves are displayed in their queue based on the origin duty location. GHC moves will show up here onced they have reached the submitted status sent by the customer and have move task orders, shipments, and service items to approve.\n", @@ -21336,6 +21496,172 @@ func init() { } } }, + "/queues/destination-requests": { + "get": { + "description": "A TOO will view this queue when they have destination requests tied to their GBLOC. This includes unapproved destination SIT service items (including shuttle) and destination address requests that are not approved.\n", + "produces": [ + "application/json" + ], + "tags": [ + "queues" + ], + "summary": "Gets queued list of all customer moves by GBLOC that have destination requests (destination SIT, shuttle, address requests)", + "operationId": "getDestinationRequestsQueue", + "parameters": [ + { + "type": "integer", + "description": "requested page of results", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "results per page", + "name": "perPage", + "in": "query" + }, + { + "enum": [ + "customerName", + "edipi", + "emplid", + "branch", + "locator", + "status", + "originDutyLocation", + "destinationDutyLocation", + "requestedMoveDate", + "appearedInTooAt", + "assignedTo", + "counselingOffice" + ], + "type": "string", + "description": "field that results should be sorted by", + "name": "sort", + "in": "query" + }, + { + "enum": [ + "asc", + "desc" + ], + "type": "string", + "description": "direction of sort order if applied", + "name": "order", + "in": "query" + }, + { + "type": "string", + "name": "branch", + "in": "query" + }, + { + "type": "string", + "name": "locator", + "in": "query" + }, + { + "type": "string", + "name": "customerName", + "in": "query" + }, + { + "type": "string", + "name": "edipi", + "in": "query" + }, + { + "type": "string", + "name": "emplid", + "in": "query" + }, + { + "uniqueItems": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "name": "originDutyLocation", + "in": "query" + }, + { + "type": "string", + "name": "destinationDutyLocation", + "in": "query" + }, + { + "type": "string", + "format": "date-time", + "name": "appearedInTooAt", + "in": "query" + }, + { + "type": "string", + "description": "filters the requested pickup date of a shipment on the move", + "name": "requestedMoveDate", + "in": "query" + }, + { + "uniqueItems": true, + "type": "array", + "items": { + "enum": [ + "APPROVALS REQUESTED" + ], + "type": "string" + }, + "description": "Filtering for the status.", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "order type", + "name": "orderType", + "in": "query" + }, + { + "type": "string", + "description": "Used to return a queue for a GBLOC other than the default of the current user. Requires the HQ role. The parameter is ignored if the requesting user does not have the necessary role.\n", + "name": "viewAsGBLOC", + "in": "query" + }, + { + "type": "string", + "description": "Used to illustrate which user is assigned to this move.\n", + "name": "assignedTo", + "in": "query" + }, + { + "type": "string", + "description": "filters using a counselingOffice name of the move", + "name": "counselingOffice", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successfully returned all moves matching the criteria", + "schema": { + "$ref": "#/definitions/QueueMovesResult" + } + }, + "403": { + "description": "The request was denied", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "500": { + "description": "A server error occurred", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, "/queues/moves": { "get": { "description": "An office TOO user will be assigned a transportation office that will determine which moves are displayed in their queue based on the origin duty location. GHC moves will show up here onced they have reached the submitted status sent by the customer and have move task orders, shipments, and service items to approve.\n", diff --git a/pkg/gen/ghcapi/ghcoperations/mymove_api.go b/pkg/gen/ghcapi/ghcoperations/mymove_api.go index c53c0fec4d7..61bda14b7d3 100644 --- a/pkg/gen/ghcapi/ghcoperations/mymove_api.go +++ b/pkg/gen/ghcapi/ghcoperations/mymove_api.go @@ -173,6 +173,9 @@ func NewMymoveAPI(spec *loads.Document) *MymoveAPI { CustomerSupportRemarksGetCustomerSupportRemarksForMoveHandler: customer_support_remarks.GetCustomerSupportRemarksForMoveHandlerFunc(func(params customer_support_remarks.GetCustomerSupportRemarksForMoveParams) middleware.Responder { return middleware.NotImplemented("operation customer_support_remarks.GetCustomerSupportRemarksForMove has not yet been implemented") }), + QueuesGetDestinationRequestsQueueHandler: queues.GetDestinationRequestsQueueHandlerFunc(func(params queues.GetDestinationRequestsQueueParams) middleware.Responder { + return middleware.NotImplemented("operation queues.GetDestinationRequestsQueue has not yet been implemented") + }), GhcDocumentsGetDocumentHandler: ghc_documents.GetDocumentHandlerFunc(func(params ghc_documents.GetDocumentParams) middleware.Responder { return middleware.NotImplemented("operation ghc_documents.GetDocument has not yet been implemented") }), @@ -509,6 +512,8 @@ type MymoveAPI struct { CustomerGetCustomerHandler customer.GetCustomerHandler // CustomerSupportRemarksGetCustomerSupportRemarksForMoveHandler sets the operation handler for the get customer support remarks for move operation CustomerSupportRemarksGetCustomerSupportRemarksForMoveHandler customer_support_remarks.GetCustomerSupportRemarksForMoveHandler + // QueuesGetDestinationRequestsQueueHandler sets the operation handler for the get destination requests queue operation + QueuesGetDestinationRequestsQueueHandler queues.GetDestinationRequestsQueueHandler // GhcDocumentsGetDocumentHandler sets the operation handler for the get document operation GhcDocumentsGetDocumentHandler ghc_documents.GetDocumentHandler // MoveTaskOrderGetEntitlementsHandler sets the operation handler for the get entitlements operation @@ -842,6 +847,9 @@ func (o *MymoveAPI) Validate() error { if o.CustomerSupportRemarksGetCustomerSupportRemarksForMoveHandler == nil { unregistered = append(unregistered, "customer_support_remarks.GetCustomerSupportRemarksForMoveHandler") } + if o.QueuesGetDestinationRequestsQueueHandler == nil { + unregistered = append(unregistered, "queues.GetDestinationRequestsQueueHandler") + } if o.GhcDocumentsGetDocumentHandler == nil { unregistered = append(unregistered, "ghc_documents.GetDocumentHandler") } @@ -1295,6 +1303,10 @@ func (o *MymoveAPI) initHandlerCache() { if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } + o.handlers["GET"]["/queues/destination-requests"] = queues.NewGetDestinationRequestsQueue(o.context, o.QueuesGetDestinationRequestsQueueHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } o.handlers["GET"]["/documents/{documentId}"] = ghc_documents.NewGetDocument(o.context, o.GhcDocumentsGetDocumentHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue.go b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue.go new file mode 100644 index 00000000000..f4fdf4135d3 --- /dev/null +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue.go @@ -0,0 +1,58 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package queues + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// GetDestinationRequestsQueueHandlerFunc turns a function with the right signature into a get destination requests queue handler +type GetDestinationRequestsQueueHandlerFunc func(GetDestinationRequestsQueueParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetDestinationRequestsQueueHandlerFunc) Handle(params GetDestinationRequestsQueueParams) middleware.Responder { + return fn(params) +} + +// GetDestinationRequestsQueueHandler interface for that can handle valid get destination requests queue params +type GetDestinationRequestsQueueHandler interface { + Handle(GetDestinationRequestsQueueParams) middleware.Responder +} + +// NewGetDestinationRequestsQueue creates a new http.Handler for the get destination requests queue operation +func NewGetDestinationRequestsQueue(ctx *middleware.Context, handler GetDestinationRequestsQueueHandler) *GetDestinationRequestsQueue { + return &GetDestinationRequestsQueue{Context: ctx, Handler: handler} +} + +/* + GetDestinationRequestsQueue swagger:route GET /queues/destination-requests queues getDestinationRequestsQueue + +Gets queued list of all customer moves by GBLOC that have destination requests (destination SIT, shuttle, address requests) + +A TOO will view this queue when they have destination requests tied to their GBLOC. This includes unapproved destination SIT service items (including shuttle) and destination address requests that are not approved. +*/ +type GetDestinationRequestsQueue struct { + Context *middleware.Context + Handler GetDestinationRequestsQueueHandler +} + +func (o *GetDestinationRequestsQueue) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetDestinationRequestsQueueParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_parameters.go b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_parameters.go new file mode 100644 index 00000000000..69171c50aed --- /dev/null +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_parameters.go @@ -0,0 +1,644 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package queues + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// NewGetDestinationRequestsQueueParams creates a new GetDestinationRequestsQueueParams object +// +// There are no default values defined in the spec. +func NewGetDestinationRequestsQueueParams() GetDestinationRequestsQueueParams { + + return GetDestinationRequestsQueueParams{} +} + +// GetDestinationRequestsQueueParams contains all the bound params for the get destination requests queue operation +// typically these are obtained from a http.Request +// +// swagger:parameters getDestinationRequestsQueue +type GetDestinationRequestsQueueParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + In: query + */ + AppearedInTooAt *strfmt.DateTime + /*Used to illustrate which user is assigned to this move. + + In: query + */ + AssignedTo *string + /* + In: query + */ + Branch *string + /*filters using a counselingOffice name of the move + In: query + */ + CounselingOffice *string + /* + In: query + */ + CustomerName *string + /* + In: query + */ + DestinationDutyLocation *string + /* + In: query + */ + Edipi *string + /* + In: query + */ + Emplid *string + /* + In: query + */ + Locator *string + /*direction of sort order if applied + In: query + */ + Order *string + /*order type + In: query + */ + OrderType *string + /* + Unique: true + In: query + Collection Format: multi + */ + OriginDutyLocation []string + /*requested page of results + In: query + */ + Page *int64 + /*results per page + In: query + */ + PerPage *int64 + /*filters the requested pickup date of a shipment on the move + In: query + */ + RequestedMoveDate *string + /*field that results should be sorted by + In: query + */ + Sort *string + /*Filtering for the status. + Unique: true + In: query + */ + Status []string + /*Used to return a queue for a GBLOC other than the default of the current user. Requires the HQ role. The parameter is ignored if the requesting user does not have the necessary role. + + In: query + */ + ViewAsGBLOC *string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetDestinationRequestsQueueParams() beforehand. +func (o *GetDestinationRequestsQueueParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + qs := runtime.Values(r.URL.Query()) + + qAppearedInTooAt, qhkAppearedInTooAt, _ := qs.GetOK("appearedInTooAt") + if err := o.bindAppearedInTooAt(qAppearedInTooAt, qhkAppearedInTooAt, 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) + } + + qBranch, qhkBranch, _ := qs.GetOK("branch") + if err := o.bindBranch(qBranch, qhkBranch, route.Formats); err != nil { + res = append(res, err) + } + + qCounselingOffice, qhkCounselingOffice, _ := qs.GetOK("counselingOffice") + if err := o.bindCounselingOffice(qCounselingOffice, qhkCounselingOffice, route.Formats); err != nil { + res = append(res, err) + } + + qCustomerName, qhkCustomerName, _ := qs.GetOK("customerName") + if err := o.bindCustomerName(qCustomerName, qhkCustomerName, route.Formats); err != nil { + res = append(res, err) + } + + qDestinationDutyLocation, qhkDestinationDutyLocation, _ := qs.GetOK("destinationDutyLocation") + if err := o.bindDestinationDutyLocation(qDestinationDutyLocation, qhkDestinationDutyLocation, route.Formats); err != nil { + res = append(res, err) + } + + qEdipi, qhkEdipi, _ := qs.GetOK("edipi") + if err := o.bindEdipi(qEdipi, qhkEdipi, route.Formats); err != nil { + res = append(res, err) + } + + qEmplid, qhkEmplid, _ := qs.GetOK("emplid") + if err := o.bindEmplid(qEmplid, qhkEmplid, route.Formats); err != nil { + res = append(res, err) + } + + qLocator, qhkLocator, _ := qs.GetOK("locator") + if err := o.bindLocator(qLocator, qhkLocator, route.Formats); err != nil { + res = append(res, err) + } + + qOrder, qhkOrder, _ := qs.GetOK("order") + if err := o.bindOrder(qOrder, qhkOrder, route.Formats); err != nil { + res = append(res, err) + } + + qOrderType, qhkOrderType, _ := qs.GetOK("orderType") + if err := o.bindOrderType(qOrderType, qhkOrderType, route.Formats); err != nil { + res = append(res, err) + } + + qOriginDutyLocation, qhkOriginDutyLocation, _ := qs.GetOK("originDutyLocation") + if err := o.bindOriginDutyLocation(qOriginDutyLocation, qhkOriginDutyLocation, route.Formats); err != nil { + res = append(res, err) + } + + qPage, qhkPage, _ := qs.GetOK("page") + if err := o.bindPage(qPage, qhkPage, route.Formats); err != nil { + res = append(res, err) + } + + qPerPage, qhkPerPage, _ := qs.GetOK("perPage") + if err := o.bindPerPage(qPerPage, qhkPerPage, route.Formats); err != nil { + res = append(res, err) + } + + qRequestedMoveDate, qhkRequestedMoveDate, _ := qs.GetOK("requestedMoveDate") + if err := o.bindRequestedMoveDate(qRequestedMoveDate, qhkRequestedMoveDate, route.Formats); err != nil { + res = append(res, err) + } + + qSort, qhkSort, _ := qs.GetOK("sort") + if err := o.bindSort(qSort, qhkSort, route.Formats); err != nil { + res = append(res, err) + } + + qStatus, qhkStatus, _ := qs.GetOK("status") + if err := o.bindStatus(qStatus, qhkStatus, route.Formats); err != nil { + res = append(res, err) + } + + qViewAsGBLOC, qhkViewAsGBLOC, _ := qs.GetOK("viewAsGBLOC") + if err := o.bindViewAsGBLOC(qViewAsGBLOC, qhkViewAsGBLOC, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindAppearedInTooAt binds and validates parameter AppearedInTooAt from query. +func (o *GetDestinationRequestsQueueParams) bindAppearedInTooAt(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 + } + + // Format: date-time + value, err := formats.Parse("date-time", raw) + if err != nil { + return errors.InvalidType("appearedInTooAt", "query", "strfmt.DateTime", raw) + } + o.AppearedInTooAt = (value.(*strfmt.DateTime)) + + if err := o.validateAppearedInTooAt(formats); err != nil { + return err + } + + return nil +} + +// validateAppearedInTooAt carries on validations for parameter AppearedInTooAt +func (o *GetDestinationRequestsQueueParams) validateAppearedInTooAt(formats strfmt.Registry) error { + + if err := validate.FormatOf("appearedInTooAt", "query", "date-time", o.AppearedInTooAt.String(), formats); err != nil { + return err + } + return nil +} + +// bindAssignedTo binds and validates parameter AssignedTo from query. +func (o *GetDestinationRequestsQueueParams) bindAssignedTo(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.AssignedTo = &raw + + return nil +} + +// bindBranch binds and validates parameter Branch from query. +func (o *GetDestinationRequestsQueueParams) bindBranch(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.Branch = &raw + + return nil +} + +// bindCounselingOffice binds and validates parameter CounselingOffice from query. +func (o *GetDestinationRequestsQueueParams) bindCounselingOffice(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.CounselingOffice = &raw + + return nil +} + +// bindCustomerName binds and validates parameter CustomerName from query. +func (o *GetDestinationRequestsQueueParams) bindCustomerName(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.CustomerName = &raw + + return nil +} + +// bindDestinationDutyLocation binds and validates parameter DestinationDutyLocation from query. +func (o *GetDestinationRequestsQueueParams) bindDestinationDutyLocation(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.DestinationDutyLocation = &raw + + return nil +} + +// bindEdipi binds and validates parameter Edipi from query. +func (o *GetDestinationRequestsQueueParams) bindEdipi(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.Edipi = &raw + + return nil +} + +// bindEmplid binds and validates parameter Emplid from query. +func (o *GetDestinationRequestsQueueParams) bindEmplid(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.Emplid = &raw + + return nil +} + +// bindLocator binds and validates parameter Locator from query. +func (o *GetDestinationRequestsQueueParams) bindLocator(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.Locator = &raw + + return nil +} + +// bindOrder binds and validates parameter Order from query. +func (o *GetDestinationRequestsQueueParams) bindOrder(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.Order = &raw + + if err := o.validateOrder(formats); err != nil { + return err + } + + return nil +} + +// validateOrder carries on validations for parameter Order +func (o *GetDestinationRequestsQueueParams) validateOrder(formats strfmt.Registry) error { + + if err := validate.EnumCase("order", "query", *o.Order, []interface{}{"asc", "desc"}, true); err != nil { + return err + } + + return nil +} + +// bindOrderType binds and validates parameter OrderType from query. +func (o *GetDestinationRequestsQueueParams) bindOrderType(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.OrderType = &raw + + return nil +} + +// bindOriginDutyLocation binds and validates array parameter OriginDutyLocation from query. +// +// Arrays are parsed according to CollectionFormat: "multi" (defaults to "csv" when empty). +func (o *GetDestinationRequestsQueueParams) bindOriginDutyLocation(rawData []string, hasKey bool, formats strfmt.Registry) error { + // CollectionFormat: multi + originDutyLocationIC := rawData + if len(originDutyLocationIC) == 0 { + return nil + } + + var originDutyLocationIR []string + for _, originDutyLocationIV := range originDutyLocationIC { + originDutyLocationI := originDutyLocationIV + + originDutyLocationIR = append(originDutyLocationIR, originDutyLocationI) + } + + o.OriginDutyLocation = originDutyLocationIR + if err := o.validateOriginDutyLocation(formats); err != nil { + return err + } + + return nil +} + +// validateOriginDutyLocation carries on validations for parameter OriginDutyLocation +func (o *GetDestinationRequestsQueueParams) validateOriginDutyLocation(formats strfmt.Registry) error { + + // uniqueItems: true + if err := validate.UniqueItems("originDutyLocation", "query", o.OriginDutyLocation); err != nil { + return err + } + return nil +} + +// bindPage binds and validates parameter Page from query. +func (o *GetDestinationRequestsQueueParams) bindPage(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 + } + + value, err := swag.ConvertInt64(raw) + if err != nil { + return errors.InvalidType("page", "query", "int64", raw) + } + o.Page = &value + + return nil +} + +// bindPerPage binds and validates parameter PerPage from query. +func (o *GetDestinationRequestsQueueParams) bindPerPage(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 + } + + value, err := swag.ConvertInt64(raw) + if err != nil { + return errors.InvalidType("perPage", "query", "int64", raw) + } + o.PerPage = &value + + return nil +} + +// bindRequestedMoveDate binds and validates parameter RequestedMoveDate from query. +func (o *GetDestinationRequestsQueueParams) bindRequestedMoveDate(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.RequestedMoveDate = &raw + + return nil +} + +// bindSort binds and validates parameter Sort from query. +func (o *GetDestinationRequestsQueueParams) bindSort(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.Sort = &raw + + if err := o.validateSort(formats); err != nil { + return err + } + + return nil +} + +// validateSort carries on validations for parameter Sort +func (o *GetDestinationRequestsQueueParams) validateSort(formats strfmt.Registry) error { + + if err := validate.EnumCase("sort", "query", *o.Sort, []interface{}{"customerName", "edipi", "emplid", "branch", "locator", "status", "originDutyLocation", "destinationDutyLocation", "requestedMoveDate", "appearedInTooAt", "assignedTo", "counselingOffice"}, true); err != nil { + return err + } + + return nil +} + +// bindStatus binds and validates array parameter Status from query. +// +// Arrays are parsed according to CollectionFormat: "" (defaults to "csv" when empty). +func (o *GetDestinationRequestsQueueParams) bindStatus(rawData []string, hasKey bool, formats strfmt.Registry) error { + var qvStatus string + if len(rawData) > 0 { + qvStatus = rawData[len(rawData)-1] + } + + // CollectionFormat: + statusIC := swag.SplitByFormat(qvStatus, "") + if len(statusIC) == 0 { + return nil + } + + var statusIR []string + for i, statusIV := range statusIC { + statusI := statusIV + + if err := validate.EnumCase(fmt.Sprintf("%s.%v", "status", i), "query", statusI, []interface{}{"APPROVALS REQUESTED"}, true); err != nil { + return err + } + + statusIR = append(statusIR, statusI) + } + + o.Status = statusIR + if err := o.validateStatus(formats); err != nil { + return err + } + + return nil +} + +// validateStatus carries on validations for parameter Status +func (o *GetDestinationRequestsQueueParams) validateStatus(formats strfmt.Registry) error { + + // uniqueItems: true + if err := validate.UniqueItems("status", "query", o.Status); err != nil { + return err + } + return nil +} + +// bindViewAsGBLOC binds and validates parameter ViewAsGBLOC from query. +func (o *GetDestinationRequestsQueueParams) bindViewAsGBLOC(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.ViewAsGBLOC = &raw + + return nil +} diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_responses.go b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_responses.go new file mode 100644 index 00000000000..ea27ea91263 --- /dev/null +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_responses.go @@ -0,0 +1,149 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package queues + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/transcom/mymove/pkg/gen/ghcmessages" +) + +// GetDestinationRequestsQueueOKCode is the HTTP code returned for type GetDestinationRequestsQueueOK +const GetDestinationRequestsQueueOKCode int = 200 + +/* +GetDestinationRequestsQueueOK Successfully returned all moves matching the criteria + +swagger:response getDestinationRequestsQueueOK +*/ +type GetDestinationRequestsQueueOK struct { + + /* + In: Body + */ + Payload *ghcmessages.QueueMovesResult `json:"body,omitempty"` +} + +// NewGetDestinationRequestsQueueOK creates GetDestinationRequestsQueueOK with default headers values +func NewGetDestinationRequestsQueueOK() *GetDestinationRequestsQueueOK { + + return &GetDestinationRequestsQueueOK{} +} + +// WithPayload adds the payload to the get destination requests queue o k response +func (o *GetDestinationRequestsQueueOK) WithPayload(payload *ghcmessages.QueueMovesResult) *GetDestinationRequestsQueueOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get destination requests queue o k response +func (o *GetDestinationRequestsQueueOK) SetPayload(payload *ghcmessages.QueueMovesResult) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetDestinationRequestsQueueOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetDestinationRequestsQueueForbiddenCode is the HTTP code returned for type GetDestinationRequestsQueueForbidden +const GetDestinationRequestsQueueForbiddenCode int = 403 + +/* +GetDestinationRequestsQueueForbidden The request was denied + +swagger:response getDestinationRequestsQueueForbidden +*/ +type GetDestinationRequestsQueueForbidden struct { + + /* + In: Body + */ + Payload *ghcmessages.Error `json:"body,omitempty"` +} + +// NewGetDestinationRequestsQueueForbidden creates GetDestinationRequestsQueueForbidden with default headers values +func NewGetDestinationRequestsQueueForbidden() *GetDestinationRequestsQueueForbidden { + + return &GetDestinationRequestsQueueForbidden{} +} + +// WithPayload adds the payload to the get destination requests queue forbidden response +func (o *GetDestinationRequestsQueueForbidden) WithPayload(payload *ghcmessages.Error) *GetDestinationRequestsQueueForbidden { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get destination requests queue forbidden response +func (o *GetDestinationRequestsQueueForbidden) SetPayload(payload *ghcmessages.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetDestinationRequestsQueueForbidden) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(403) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetDestinationRequestsQueueInternalServerErrorCode is the HTTP code returned for type GetDestinationRequestsQueueInternalServerError +const GetDestinationRequestsQueueInternalServerErrorCode int = 500 + +/* +GetDestinationRequestsQueueInternalServerError A server error occurred + +swagger:response getDestinationRequestsQueueInternalServerError +*/ +type GetDestinationRequestsQueueInternalServerError struct { + + /* + In: Body + */ + Payload *ghcmessages.Error `json:"body,omitempty"` +} + +// NewGetDestinationRequestsQueueInternalServerError creates GetDestinationRequestsQueueInternalServerError with default headers values +func NewGetDestinationRequestsQueueInternalServerError() *GetDestinationRequestsQueueInternalServerError { + + return &GetDestinationRequestsQueueInternalServerError{} +} + +// WithPayload adds the payload to the get destination requests queue internal server error response +func (o *GetDestinationRequestsQueueInternalServerError) WithPayload(payload *ghcmessages.Error) *GetDestinationRequestsQueueInternalServerError { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get destination requests queue internal server error response +func (o *GetDestinationRequestsQueueInternalServerError) SetPayload(payload *ghcmessages.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetDestinationRequestsQueueInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_urlbuilder.go b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_urlbuilder.go new file mode 100644 index 00000000000..c267cfe2aa8 --- /dev/null +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_urlbuilder.go @@ -0,0 +1,274 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package queues + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// GetDestinationRequestsQueueURL generates an URL for the get destination requests queue operation +type GetDestinationRequestsQueueURL struct { + AppearedInTooAt *strfmt.DateTime + AssignedTo *string + Branch *string + CounselingOffice *string + CustomerName *string + DestinationDutyLocation *string + Edipi *string + Emplid *string + Locator *string + Order *string + OrderType *string + OriginDutyLocation []string + Page *int64 + PerPage *int64 + RequestedMoveDate *string + Sort *string + Status []string + ViewAsGBLOC *string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetDestinationRequestsQueueURL) WithBasePath(bp string) *GetDestinationRequestsQueueURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetDestinationRequestsQueueURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetDestinationRequestsQueueURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/queues/destination-requests" + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/ghc/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + qs := make(url.Values) + + var appearedInTooAtQ string + if o.AppearedInTooAt != nil { + appearedInTooAtQ = o.AppearedInTooAt.String() + } + if appearedInTooAtQ != "" { + qs.Set("appearedInTooAt", appearedInTooAtQ) + } + + var assignedToQ string + if o.AssignedTo != nil { + assignedToQ = *o.AssignedTo + } + if assignedToQ != "" { + qs.Set("assignedTo", assignedToQ) + } + + var branchQ string + if o.Branch != nil { + branchQ = *o.Branch + } + if branchQ != "" { + qs.Set("branch", branchQ) + } + + var counselingOfficeQ string + if o.CounselingOffice != nil { + counselingOfficeQ = *o.CounselingOffice + } + if counselingOfficeQ != "" { + qs.Set("counselingOffice", counselingOfficeQ) + } + + var customerNameQ string + if o.CustomerName != nil { + customerNameQ = *o.CustomerName + } + if customerNameQ != "" { + qs.Set("customerName", customerNameQ) + } + + var destinationDutyLocationQ string + if o.DestinationDutyLocation != nil { + destinationDutyLocationQ = *o.DestinationDutyLocation + } + if destinationDutyLocationQ != "" { + qs.Set("destinationDutyLocation", destinationDutyLocationQ) + } + + var edipiQ string + if o.Edipi != nil { + edipiQ = *o.Edipi + } + if edipiQ != "" { + qs.Set("edipi", edipiQ) + } + + var emplidQ string + if o.Emplid != nil { + emplidQ = *o.Emplid + } + if emplidQ != "" { + qs.Set("emplid", emplidQ) + } + + var locatorQ string + if o.Locator != nil { + locatorQ = *o.Locator + } + if locatorQ != "" { + qs.Set("locator", locatorQ) + } + + var orderQ string + if o.Order != nil { + orderQ = *o.Order + } + if orderQ != "" { + qs.Set("order", orderQ) + } + + var orderTypeQ string + if o.OrderType != nil { + orderTypeQ = *o.OrderType + } + if orderTypeQ != "" { + qs.Set("orderType", orderTypeQ) + } + + var originDutyLocationIR []string + for _, originDutyLocationI := range o.OriginDutyLocation { + originDutyLocationIS := originDutyLocationI + if originDutyLocationIS != "" { + originDutyLocationIR = append(originDutyLocationIR, originDutyLocationIS) + } + } + + originDutyLocation := swag.JoinByFormat(originDutyLocationIR, "multi") + + for _, qsv := range originDutyLocation { + qs.Add("originDutyLocation", qsv) + } + + var pageQ string + if o.Page != nil { + pageQ = swag.FormatInt64(*o.Page) + } + if pageQ != "" { + qs.Set("page", pageQ) + } + + var perPageQ string + if o.PerPage != nil { + perPageQ = swag.FormatInt64(*o.PerPage) + } + if perPageQ != "" { + qs.Set("perPage", perPageQ) + } + + var requestedMoveDateQ string + if o.RequestedMoveDate != nil { + requestedMoveDateQ = *o.RequestedMoveDate + } + if requestedMoveDateQ != "" { + qs.Set("requestedMoveDate", requestedMoveDateQ) + } + + var sortQ string + if o.Sort != nil { + sortQ = *o.Sort + } + if sortQ != "" { + qs.Set("sort", sortQ) + } + + var statusIR []string + for _, statusI := range o.Status { + statusIS := statusI + if statusIS != "" { + statusIR = append(statusIR, statusIS) + } + } + + status := swag.JoinByFormat(statusIR, "") + + if len(status) > 0 { + qsv := status[0] + if qsv != "" { + qs.Set("status", qsv) + } + } + + var viewAsGBLOCQ string + if o.ViewAsGBLOC != nil { + viewAsGBLOCQ = *o.ViewAsGBLOC + } + if viewAsGBLOCQ != "" { + qs.Set("viewAsGBLOC", viewAsGBLOCQ) + } + + _result.RawQuery = qs.Encode() + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetDestinationRequestsQueueURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetDestinationRequestsQueueURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetDestinationRequestsQueueURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetDestinationRequestsQueueURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetDestinationRequestsQueueURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetDestinationRequestsQueueURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/pkg/handlers/ghcapi/api.go b/pkg/handlers/ghcapi/api.go index 38ea0a31b64..9c5d287fab8 100644 --- a/pkg/handlers/ghcapi/api.go +++ b/pkg/handlers/ghcapi/api.go @@ -548,6 +548,13 @@ func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { officeusercreator.NewOfficeUserFetcherPop(), } + ghcAPI.QueuesGetDestinationRequestsQueueHandler = GetDestinationRequestsQueueHandler{ + handlerConfig, + order.NewOrderFetcher(), + movelocker.NewMoveUnlocker(), + officeusercreator.NewOfficeUserFetcherPop(), + } + ghcAPI.QueuesListPrimeMovesHandler = ListPrimeMovesHandler{ handlerConfig, movetaskorder.NewMoveTaskOrderFetcher(), diff --git a/pkg/handlers/ghcapi/queues.go b/pkg/handlers/ghcapi/queues.go index ed4980df650..ab48a061fc1 100644 --- a/pkg/handlers/ghcapi/queues.go +++ b/pkg/handlers/ghcapi/queues.go @@ -178,6 +178,132 @@ func (h GetMovesQueueHandler) Handle(params queues.GetMovesQueueParams) middlewa }) } +// GetDestinationRequestsQueueHandler returns the moves for the TOO queue user via GET /queues/destination-requests +type GetDestinationRequestsQueueHandler struct { + handlers.HandlerConfig + services.OrderFetcher + services.MoveUnlocker + services.OfficeUserFetcherPop +} + +// Handle returns the paginated list of moves with destination requests for a TOO user +func (h GetDestinationRequestsQueueHandler) Handle(params queues.GetDestinationRequestsQueueParams) middleware.Responder { + return h.AuditableAppContextFromRequestWithErrors(params.HTTPRequest, + func(appCtx appcontext.AppContext) (middleware.Responder, error) { + if !appCtx.Session().IsOfficeUser() || + (!appCtx.Session().Roles.HasRole(roles.RoleTypeTOO) && !appCtx.Session().Roles.HasRole(roles.RoleTypeHQ)) { + forbiddenErr := apperror.NewForbiddenError( + "user is not authenticated with TOO or HQ office role", + ) + appCtx.Logger().Error(forbiddenErr.Error()) + return queues.NewGetDestinationRequestsQueueForbidden(), forbiddenErr + } + + ListOrderParams := services.ListOrderParams{ + Branch: params.Branch, + Locator: params.Locator, + Edipi: params.Edipi, + Emplid: params.Emplid, + CustomerName: params.CustomerName, + DestinationDutyLocation: params.DestinationDutyLocation, + OriginDutyLocation: params.OriginDutyLocation, + AppearedInTOOAt: handlers.FmtDateTimePtrToPopPtr(params.AppearedInTooAt), + RequestedMoveDate: params.RequestedMoveDate, + Status: params.Status, + Page: params.Page, + PerPage: params.PerPage, + Sort: params.Sort, + Order: params.Order, + OrderType: params.OrderType, + TOOAssignedUser: params.AssignedTo, + CounselingOffice: params.CounselingOffice, + } + + if params.Status == nil { + ListOrderParams.Status = []string{string(models.MoveStatusAPPROVALSREQUESTED)} + } + + // 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 for perPage. + if params.Page == nil { + ListOrderParams.Page = models.Int64Pointer(1) + } + // Same for perPage + if params.PerPage == nil { + ListOrderParams.PerPage = models.Int64Pointer(20) + } + + if params.ViewAsGBLOC != nil && appCtx.Session().Roles.HasRole(roles.RoleTypeHQ) { + ListOrderParams.ViewAsGBLOC = params.ViewAsGBLOC + } + + moves, count, err := h.OrderFetcher.ListDestinationRequestsOrders( + appCtx, + appCtx.Session().OfficeUserID, + roles.RoleTypeTOO, + &ListOrderParams, + ) + if err != nil { + appCtx.Logger(). + Error("error fetching list of moves for office user", zap.Error(err)) + return queues.NewGetDestinationRequestsQueueInternalServerError(), err + } + + var officeUser models.OfficeUser + if appCtx.Session().OfficeUserID != uuid.Nil { + officeUser, err = h.OfficeUserFetcherPop.FetchOfficeUserByID(appCtx, appCtx.Session().OfficeUserID) + if err != nil { + appCtx.Logger().Error("Error retrieving office user", zap.Error(err)) + return queues.NewGetDestinationRequestsQueueInternalServerError(), err + } + } + privileges, err := models.FetchPrivilegesForUser(appCtx.DB(), appCtx.Session().UserID) + if err != nil { + appCtx.Logger().Error("Error retreiving user privileges", zap.Error(err)) + } + officeUser.User.Privileges = privileges + officeUser.User.Roles = appCtx.Session().Roles + + if err != nil { + appCtx.Logger(). + Error("error fetching office users", zap.Error(err)) + return queues.NewGetDestinationRequestsQueueInternalServerError(), err + } + + // if the TOO/office user is accessing the queue, we need to unlock move/moves they have locked + if appCtx.Session().IsOfficeUser() { + officeUserID := appCtx.Session().OfficeUserID + for i, move := range moves { + lockedOfficeUserID := move.LockedByOfficeUserID + if lockedOfficeUserID != nil && *lockedOfficeUserID == officeUserID { + copyOfMove := move + unlockedMove, err := h.UnlockMove(appCtx, ©OfMove, officeUserID) + if err != nil { + return queues.NewGetDestinationRequestsQueueInternalServerError(), err + } + moves[i] = *unlockedMove + } + } + // checking if moves that are NOT in their queue are locked by the user (using search, etc) + err := h.CheckForLockedMovesAndUnlock(appCtx, officeUserID) + if err != nil { + appCtx.Logger().Error(fmt.Sprintf("failed to unlock moves for office user ID: %s", officeUserID), zap.Error(err)) + } + } + + officeUsers := models.OfficeUsers{officeUser} + queueMoves := payloads.QueueMoves(moves, officeUsers, nil, officeUser, nil) + + result := &ghcmessages.QueueMovesResult{ + Page: *ListOrderParams.Page, + PerPage: *ListOrderParams.PerPage, + TotalCount: int64(count), + QueueMoves: *queueMoves, + } + + return queues.NewGetDestinationRequestsQueueOK().WithPayload(result), nil + }) +} + // ListMovesHandler lists moves with the option to filter since a particular date. Optimized ver. type ListPrimeMovesHandler struct { handlers.HandlerConfig diff --git a/pkg/services/mocks/OrderFetcher.go b/pkg/services/mocks/OrderFetcher.go index c7eb39d01ce..012df5ccb1b 100644 --- a/pkg/services/mocks/OrderFetcher.go +++ b/pkg/services/mocks/OrderFetcher.go @@ -80,6 +80,43 @@ func (_m *OrderFetcher) ListAllOrderLocations(appCtx appcontext.AppContext, offi return r0, r1 } +// ListDestinationRequestsOrders provides a mock function with given fields: appCtx, officeUserID, role, params +func (_m *OrderFetcher) ListDestinationRequestsOrders(appCtx appcontext.AppContext, officeUserID uuid.UUID, role roles.RoleType, params *services.ListOrderParams) ([]models.Move, int, error) { + ret := _m.Called(appCtx, officeUserID, role, params) + + if len(ret) == 0 { + panic("no return value specified for ListDestinationRequestsOrders") + } + + var r0 []models.Move + var r1 int + var r2 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, uuid.UUID, roles.RoleType, *services.ListOrderParams) ([]models.Move, int, error)); ok { + return rf(appCtx, officeUserID, role, params) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, uuid.UUID, roles.RoleType, *services.ListOrderParams) []models.Move); ok { + r0 = rf(appCtx, officeUserID, role, params) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.Move) + } + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, uuid.UUID, roles.RoleType, *services.ListOrderParams) int); ok { + r1 = rf(appCtx, officeUserID, role, params) + } else { + r1 = ret.Get(1).(int) + } + + if rf, ok := ret.Get(2).(func(appcontext.AppContext, uuid.UUID, roles.RoleType, *services.ListOrderParams) error); ok { + r2 = rf(appCtx, officeUserID, role, params) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // ListOrders provides a mock function with given fields: appCtx, officeUserID, role, params func (_m *OrderFetcher) ListOrders(appCtx appcontext.AppContext, officeUserID uuid.UUID, role roles.RoleType, params *services.ListOrderParams) ([]models.Move, int, error) { ret := _m.Called(appCtx, officeUserID, role, params) diff --git a/pkg/services/order.go b/pkg/services/order.go index c1bd25d1b84..217f3e069f2 100644 --- a/pkg/services/order.go +++ b/pkg/services/order.go @@ -20,6 +20,7 @@ import ( type OrderFetcher interface { FetchOrder(appCtx appcontext.AppContext, orderID uuid.UUID) (*models.Order, error) ListOrders(appCtx appcontext.AppContext, officeUserID uuid.UUID, role roles.RoleType, params *ListOrderParams) ([]models.Move, int, error) + ListDestinationRequestsOrders(appCtx appcontext.AppContext, officeUserID uuid.UUID, role roles.RoleType, params *ListOrderParams) ([]models.Move, int, error) ListAllOrderLocations(appCtx appcontext.AppContext, officeUserID uuid.UUID, params *ListOrderParams) ([]models.Move, error) } diff --git a/pkg/services/order/order_fetcher.go b/pkg/services/order/order_fetcher.go index 9e1c438883a..cfbcebfec0d 100644 --- a/pkg/services/order/order_fetcher.go +++ b/pkg/services/order/order_fetcher.go @@ -306,7 +306,153 @@ func (f orderFetcher) ListOrders(appCtx appcontext.AppContext, officeUserID uuid return moves, count, nil } -// TODO: Update query to select distinct duty locations +func (f orderFetcher) ListDestinationRequestsOrders(appCtx appcontext.AppContext, officeUserID uuid.UUID, role roles.RoleType, params *services.ListOrderParams) ([]models.Move, int, error) { + var moves []models.Move + + var officeUserGbloc string + if params.ViewAsGBLOC != nil { + officeUserGbloc = *params.ViewAsGBLOC + } else { + var gblocErr error + gblocFetcher := officeuser.NewOfficeUserGblocFetcher() + officeUserGbloc, gblocErr = gblocFetcher.FetchGblocForOfficeUser(appCtx, officeUserID) + if gblocErr != nil { + return []models.Move{}, 0, gblocErr + } + } + + ppmCloseoutGblocs := officeUserGbloc == "NAVY" || officeUserGbloc == "TVCB" || officeUserGbloc == "USCG" + + branchQuery := branchFilter(params.Branch, false, ppmCloseoutGblocs) + + // If the user is associated with the USMC GBLOC we want to show them ALL the USMC moves, so let's override here. + // We also only want to do the gbloc filtering thing if we aren't a USMC user, which we cover with the else. + // var gblocQuery QueryOption + var gblocToFilterBy *string + if officeUserGbloc == "USMC" { + branchQuery = branchFilter(models.StringPointer(string(models.AffiliationMARINES)), false, ppmCloseoutGblocs) + gblocToFilterBy = params.OriginGBLOC + } else { + gblocToFilterBy = &officeUserGbloc + } + + gblocQuery := gblocFilterForTOO(gblocToFilterBy) + + locatorQuery := locatorFilter(params.Locator) + dodIDQuery := dodIDFilter(params.Edipi) + emplidQuery := emplidFilter(params.Emplid) + customerNameQuery := customerNameFilter(params.CustomerName) + originDutyLocationQuery := originDutyLocationFilter(params.OriginDutyLocation) + destinationDutyLocationQuery := destinationDutyLocationFilter(params.DestinationDutyLocation) + moveStatusQuery := moveStatusFilter(params.Status) + submittedAtQuery := submittedAtFilter(params.SubmittedAt) + appearedInTOOAtQuery := appearedInTOOAtFilter(params.AppearedInTOOAt) + requestedMoveDateQuery := requestedMoveDateFilter(params.RequestedMoveDate) + closeoutInitiatedQuery := closeoutInitiatedFilter(params.CloseoutInitiated) + closeoutLocationQuery := closeoutLocationFilter(params.CloseoutLocation, ppmCloseoutGblocs) + ppmTypeQuery := ppmTypeFilter(params.PPMType) + ppmStatusQuery := ppmStatusFilter(params.PPMStatus) + scAssignedUserQuery := scAssignedUserFilter(params.SCAssignedUser) + tooAssignedUserQuery := tooAssignedUserFilter(params.TOOAssignedUser) + sortOrderQuery := sortOrder(params.Sort, params.Order, ppmCloseoutGblocs) + counselingQuery := counselingOfficeFilter(params.CounselingOffice) + + // Adding to an array so we can iterate over them and apply the filters after the query structure is set below + options := [20]QueryOption{branchQuery, locatorQuery, dodIDQuery, emplidQuery, customerNameQuery, originDutyLocationQuery, destinationDutyLocationQuery, moveStatusQuery, gblocQuery, submittedAtQuery, appearedInTOOAtQuery, requestedMoveDateQuery, ppmTypeQuery, closeoutInitiatedQuery, closeoutLocationQuery, ppmStatusQuery, sortOrderQuery, scAssignedUserQuery, tooAssignedUserQuery, counselingQuery} + + // we want to set the query up to where it only shows moves that have orders.destination_gbloc that are in the office user's GBLOC (some exclusions apply) + query := appCtx.DB().Q().Scope(utilities.ExcludeDeletedScope(models.MTOShipment{})).EagerPreload( + "Orders.ServiceMember", + "Orders.NewDutyLocation.Address", + "Orders.OriginDutyLocation.Address", + "Orders.Entitlement", + "MTOShipments.DeliveryAddressUpdate", + "MTOServiceItems.ReService.Code", + "ShipmentGBLOC", + "MTOShipments.PPMShipment", + "CloseoutOffice", + "LockedByOfficeUser", + "CounselingOffice", + "SCAssignedUser", + "TOOAssignedUser", + ).InnerJoin("orders", "orders.id = moves.orders_id"). + InnerJoin("service_members", "orders.service_member_id = service_members.id"). + InnerJoin("mto_shipments", "moves.id = mto_shipments.move_id"). + InnerJoin("mto_service_items", "mto_shipments.id = mto_service_items.mto_shipment_id"). + InnerJoin("re_services", "mto_service_items.re_service_id = re_services.id"). + InnerJoin("duty_locations as origin_dl", "orders.origin_duty_location_id = origin_dl.id"). + LeftJoin("transportation_offices as origin_to", "origin_dl.transportation_office_id = origin_to.id"). + LeftJoin("move_to_gbloc", "move_to_gbloc.move_id = moves.id"). + LeftJoin("duty_locations as dest_dl", "dest_dl.id = orders.new_duty_location_id"). + LeftJoin("office_users", "office_users.id = moves.locked_by"). + LeftJoin("transportation_offices", "moves.counseling_transportation_office_id = transportation_offices.id"). + LeftJoin("office_users as assigned_user", "moves.too_assigned_id = assigned_user.id"). + LeftJoin("ppm_shipments", "ppm_shipments.shipment_id = mto_shipments.id"). + LeftJoin("shipment_address_updates", "shipment_address_updates.shipment_id = mto_shipments.id"). + Where("moves.status = 'APPROVALS REQUESTED' "+ + "AND mto_service_items.status = 'SUBMITTED' "+ + "AND re_services.code IN ('DDFSIT', 'DDASIT', 'DDDSIT', 'DDSHUT', 'DDSFSC') "+ + "OR shipment_address_updates.status = 'REQUESTED'"). + Where("orders.destination_gbloc = ?", officeUserGbloc). + Where("moves.show = ?", models.BoolPointer(true)) + + for _, option := range options { + if option != nil { + option(query) + } + } + + // Pass zeros into paginate in this case. Which will give us 1 page and 20 per page respectively + if params.Page == nil { + params.Page = models.Int64Pointer(0) + } + if params.PerPage == nil { + params.PerPage = models.Int64Pointer(0) + } + + var groupByColumms []string + groupByColumms = append(groupByColumms, "service_members.id", "orders.id", "origin_dl.id") + + if params.Sort != nil && *params.Sort == "originDutyLocation" { + groupByColumms = append(groupByColumms, "origin_dl.name") + } + if params.Sort != nil && *params.Sort == "destinationDutyLocation" { + groupByColumms = append(groupByColumms, "dest_dl.name") + } + if params.Sort != nil && *params.Sort == "originGBLOC" { + groupByColumms = append(groupByColumms, "origin_to.id") + } + if params.Sort != nil && *params.Sort == "counselingOffice" { + groupByColumms = append(groupByColumms, "transportation_offices.id") + } + if params.Sort != nil && *params.Sort == "assignedTo" { + groupByColumms = append(groupByColumms, "assigned_user.last_name", "assigned_user.first_name") + } + + err := query.GroupBy("moves.id", groupByColumms...).Paginate(int(*params.Page), int(*params.PerPage)).All(&moves) + if err != nil { + return []models.Move{}, 0, err + } + + count := query.Paginator.TotalEntriesSize + + for i := range moves { + if moves[i].Orders.OriginDutyLocation != nil { + loadErr := appCtx.DB().Load(moves[i].Orders.OriginDutyLocation, "TransportationOffice") + if loadErr != nil { + return []models.Move{}, 0, err + } + } + + err := appCtx.DB().Load(&moves[i].Orders.ServiceMember, "BackupContacts") + if err != nil { + return []models.Move{}, 0, err + } + } + + return moves, count, nil +} + func (f orderFetcher) ListAllOrderLocations(appCtx appcontext.AppContext, officeUserID uuid.UUID, params *services.ListOrderParams) ([]models.Move, error) { var moves []models.Move var err error diff --git a/src/constants/routes.js b/src/constants/routes.js index 1c4121aef59..a018cef31ea 100644 --- a/src/constants/routes.js +++ b/src/constants/routes.js @@ -4,8 +4,8 @@ export const generalRoutes = { REQUEST_ACCOUNT: '/request-account', PRIVACY_SECURITY_POLICY_PATH: '/privacy-and-security-policy', ACCESSIBILITY_PATH: '/accessibility', - QUEUE_SEARCH_PATH: 'Search', - BASE_QUEUE_SEARCH_PATH: '/Search', + QUEUE_SEARCH_PATH: 'search', + BASE_QUEUE_SEARCH_PATH: '/search', }; export const customerRoutes = { @@ -120,6 +120,8 @@ export const tooRoutes = { BASE_SHIPMENT_EDIT_PATH: `${BASE_MOVES_PATH}/shipments/:shipmentId`, MOVE_QUEUE: `move-queue`, BASE_MOVE_QUEUE: `/move-queue`, + BASE_DESTINATION_REQUESTS_QUEUE: `/destination-requests`, + DESTINATION_REQUESTS_QUEUE: `destination-requests`, SHIPMENT_EDIT_PATH: 'shipments/:shipmentId', BASE_MOVE_VIEW_PATH: `${BASE_MOVES_PATH}/details`, MOVE_VIEW_PATH: 'details', diff --git a/src/hooks/queries.js b/src/hooks/queries.js index ce469702359..749f7602a91 100644 --- a/src/hooks/queries.js +++ b/src/hooks/queries.js @@ -34,6 +34,7 @@ import { getPPMActualWeight, searchCustomers, getGBLOCs, + getDestinationRequestsQueue, getBulkAssignmentData, } from 'services/ghcApi'; import { getLoggedInUserQueries } from 'services/internalApi'; @@ -586,6 +587,28 @@ export const useMovesQueueQueries = ({ }; }; +export const useDestinationRequestsQueueQueries = ({ + sort, + order, + filters = [], + currentPage = PAGINATION_PAGE_DEFAULT, + currentPageSize = PAGINATION_PAGE_SIZE_DEFAULT, + viewAsGBLOC, +}) => { + const { data = {}, ...movesQueueQuery } = useQuery( + [MOVES_QUEUE, { sort, order, filters, currentPage, currentPageSize, viewAsGBLOC }], + ({ queryKey }) => getDestinationRequestsQueue(...queryKey), + ); + const { isLoading, isError, isSuccess } = movesQueueQuery; + const { queueMoves, ...dataProps } = data; + return { + queueResult: { data: queueMoves, ...dataProps }, + isLoading, + isError, + isSuccess, + }; +}; + export const useServicesCounselingQueuePPMQueries = ({ sort, order, diff --git a/src/pages/Office/MoveQueue/MoveQueue.jsx b/src/pages/Office/MoveQueue/MoveQueue.jsx index 95b703189fc..e0c08868603 100644 --- a/src/pages/Office/MoveQueue/MoveQueue.jsx +++ b/src/pages/Office/MoveQueue/MoveQueue.jsx @@ -6,8 +6,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import styles from './MoveQueue.module.scss'; import { createHeader } from 'components/Table/utils'; -import { useMovesQueueQueries, useUserQueries, useMoveSearchQueries } from 'hooks/queries'; -import { getMovesQueue } from 'services/ghcApi'; +import { + useMovesQueueQueries, + useUserQueries, + useMoveSearchQueries, + useDestinationRequestsQueueQueries, +} from 'hooks/queries'; +import { getDestinationRequestsQueue, getMovesQueue } from 'services/ghcApi'; import { formatDateFromIso, serviceMemberAgencyLabel } from 'utils/formatters'; import MultiSelectCheckBoxFilter from 'components/Table/Filters/MultiSelectCheckBoxFilter'; import SelectFilter from 'components/Table/Filters/SelectFilter'; @@ -270,6 +275,15 @@ const MoveQueue = ({ isQueueManagementFFEnabled, userPrivileges, isBulkAssignmen Task Order Queue , + (isActive ? 'usa-current' : '')} + to={tooRoutes.BASE_DESTINATION_REQUESTS_QUEUE} + > + + Destination Requests Queue + + , (isActive ? 'usa-current' : '')} @@ -338,6 +352,32 @@ const MoveQueue = ({ isQueueManagementFFEnabled, userPrivileges, isBulkAssignmen ); } + if (queueType === tooRoutes.DESTINATION_REQUESTS_QUEUE) { + return ( +
+ {renderNavBar()} + +
+ ); + } return ; }; diff --git a/src/services/ghcApi.js b/src/services/ghcApi.js index 1b787b7b8ad..d405122099f 100644 --- a/src/services/ghcApi.js +++ b/src/services/ghcApi.js @@ -632,6 +632,22 @@ export async function getMovesQueue( ); } +export async function getDestinationRequestsQueue( + key, + { sort, order, filters = [], currentPage = 1, currentPageSize = 20, viewAsGBLOC }, +) { + const operationPath = 'queues.getDestinationRequestsQueue'; + const paramFilters = {}; + filters.forEach((filter) => { + paramFilters[`${filter.id}`] = filter.value; + }); + return makeGHCRequest( + operationPath, + { sort, order, page: currentPage, perPage: currentPageSize, viewAsGBLOC, ...paramFilters }, + { schemaKey: 'queueMovesResult', normalize: false }, + ); +} + export async function getServicesCounselingQueue( key, { sort, order, filters = [], currentPage = 1, currentPageSize = 20, needsPPMCloseout = false, viewAsGBLOC }, diff --git a/swagger-def/ghc.yaml b/swagger-def/ghc.yaml index df06a4ca220..63f6083a887 100644 --- a/swagger-def/ghc.yaml +++ b/swagger-def/ghc.yaml @@ -3735,6 +3735,118 @@ paths: $ref: '#/responses/PermissionDenied' '500': $ref: '#/responses/ServerError' + /queues/destination-requests: + get: + produces: + - application/json + summary: Gets queued list of all customer moves by GBLOC that have destination requests (destination SIT, shuttle, address requests) + description: > + A TOO will view this queue when they have destination requests tied to their GBLOC. This includes unapproved destination SIT service items (including shuttle) and destination address requests that are not approved. + operationId: getDestinationRequestsQueue + tags: + - queues + parameters: + - in: query + name: page + type: integer + description: requested page of results + - in: query + name: perPage + type: integer + description: results per page + - in: query + name: sort + type: string + enum: + [ + customerName, + edipi, + emplid, + branch, + locator, + status, + originDutyLocation, + destinationDutyLocation, + requestedMoveDate, + appearedInTooAt, + assignedTo, + counselingOffice, + ] + description: field that results should be sorted by + - in: query + name: order + type: string + enum: [asc, desc] + description: direction of sort order if applied + - in: query + name: branch + type: string + - in: query + name: locator + type: string + - in: query + name: customerName + type: string + - in: query + name: edipi + type: string + - in: query + name: emplid + type: string + - in: query + name: originDutyLocation + type: array + uniqueItems: true + collectionFormat: multi + items: + type: string + - in: query + name: destinationDutyLocation + type: string + - in: query + name: appearedInTooAt + type: string + format: date-time + - in: query + name: requestedMoveDate + type: string + description: filters the requested pickup date of a shipment on the move + - in: query + name: status + type: array + description: Filtering for the status. + uniqueItems: true + items: + type: string + enum: + - APPROVALS REQUESTED + - in: query + name: orderType + type: string + description: order type + - in: query + name: viewAsGBLOC + type: string + description: | + Used to return a queue for a GBLOC other than the default of the current user. Requires the HQ role. The parameter is ignored if the requesting user does not have the necessary role. + - in: query + name: assignedTo + type: string + description: | + Used to illustrate which user is assigned to this move. + - in: query + name: counselingOffice + type: string + description: filters using a counselingOffice name of the move + responses: + '200': + description: Successfully returned all moves matching the criteria + schema: + $ref: '#/definitions/QueueMovesResult' + '403': + $ref: '#/responses/PermissionDenied' + '500': + $ref: '#/responses/ServerError' /queues/payment-requests: get: produces: diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml index a92ed3016a6..6df16f2884f 100644 --- a/swagger/ghc.yaml +++ b/swagger/ghc.yaml @@ -3901,6 +3901,125 @@ paths: $ref: '#/responses/PermissionDenied' '500': $ref: '#/responses/ServerError' + /queues/destination-requests: + get: + produces: + - application/json + summary: >- + Gets queued list of all customer moves by GBLOC that have destination + requests (destination SIT, shuttle, address requests) + description: > + A TOO will view this queue when they have destination requests tied to + their GBLOC. This includes unapproved destination SIT service items + (including shuttle) and destination address requests that are not + approved. + operationId: getDestinationRequestsQueue + tags: + - queues + parameters: + - in: query + name: page + type: integer + description: requested page of results + - in: query + name: perPage + type: integer + description: results per page + - in: query + name: sort + type: string + enum: + - customerName + - edipi + - emplid + - branch + - locator + - status + - originDutyLocation + - destinationDutyLocation + - requestedMoveDate + - appearedInTooAt + - assignedTo + - counselingOffice + description: field that results should be sorted by + - in: query + name: order + type: string + enum: + - asc + - desc + description: direction of sort order if applied + - in: query + name: branch + type: string + - in: query + name: locator + type: string + - in: query + name: customerName + type: string + - in: query + name: edipi + type: string + - in: query + name: emplid + type: string + - in: query + name: originDutyLocation + type: array + uniqueItems: true + collectionFormat: multi + items: + type: string + - in: query + name: destinationDutyLocation + type: string + - in: query + name: appearedInTooAt + type: string + format: date-time + - in: query + name: requestedMoveDate + type: string + description: filters the requested pickup date of a shipment on the move + - in: query + name: status + type: array + description: Filtering for the status. + uniqueItems: true + items: + type: string + enum: + - APPROVALS REQUESTED + - in: query + name: orderType + type: string + description: order type + - in: query + name: viewAsGBLOC + type: string + description: > + Used to return a queue for a GBLOC other than the default of the + current user. Requires the HQ role. The parameter is ignored if the + requesting user does not have the necessary role. + - in: query + name: assignedTo + type: string + description: | + Used to illustrate which user is assigned to this move. + - in: query + name: counselingOffice + type: string + description: filters using a counselingOffice name of the move + responses: + '200': + description: Successfully returned all moves matching the criteria + schema: + $ref: '#/definitions/QueueMovesResult' + '403': + $ref: '#/responses/PermissionDenied' + '500': + $ref: '#/responses/ServerError' /queues/payment-requests: get: produces: From 263e5f652927c084f237d56e41e1a9d0f6ebfff0 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Fri, 24 Jan 2025 22:19:34 +0000 Subject: [PATCH 02/15] most of it is working, just need to get clarification on move_to_gblocs, add tests, and refine refine refine --- ...73216_add_destination_queue_db_func.up.sql | 206 +++++++++++++----- pkg/gen/ghcapi/embedded_spec.go | 4 + ...t_destination_requests_queue_parameters.go | 2 +- .../internal/payloads/model_to_payload.go | 2 +- pkg/models/move.go | 6 +- pkg/models/mto_shipments.go | 136 ++++++------ pkg/models/order.go | 4 +- pkg/services/order/order_fetcher.go | 202 ++++++++--------- swagger-def/ghc.yaml | 2 + swagger/ghc.yaml | 2 + 10 files changed, 325 insertions(+), 241 deletions(-) diff --git a/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql b/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql index 8808b472e68..f6c40773200 100644 --- a/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql +++ b/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql @@ -1,64 +1,139 @@ +-- database function that returns a list of moves that have destination requests +-- this includes shipment address update requests & destination service items CREATE OR REPLACE FUNCTION get_destination_queue( - move_code TEXT DEFAULT NULL, -- Search parameter for Move Code - input_move_id UUID DEFAULT NULL, -- Search parameter for Move ID - page INTEGER DEFAULT 1, -- Page number for pagination - per_page INTEGER DEFAULT 20, -- Number of results per page - branch TEXT DEFAULT NULL, -- Filter: service_member.affiliation - edipi TEXT DEFAULT NULL, -- Filter: service_member.edipi - emplid TEXT DEFAULT NULL, -- Filter: service_member.emplid - customer_name TEXT DEFAULT NULL, -- Filter: service_member.first_name + last_name - destination_duty_location TEXT DEFAULT NULL,-- Filter: orders.new_duty_location_id.name - origin_duty_location TEXT DEFAULT NULL, -- Filter: orders.origin_duty_location_id.name - origin_gbloc TEXT DEFAULT NULL, -- Filter: move.counseling_office_transportation_office.gbloc - submitted_at TIMESTAMP DEFAULT NULL, -- Filter: moves.submitted_at - appeared_in_too_at TIMESTAMP DEFAULT NULL, -- Filter: moves.appeared_in_too_at - too_assigned_user TEXT DEFAULT NULL -- Filter: moves.too_assigned_id -> office_users.first_name + last_name + user_gbloc TEXT DEFAULT NULL, + customer_name TEXT DEFAULT NULL, + edipi TEXT DEFAULT NULL, + emplid TEXT DEFAULT NULL, + m_status TEXT[] DEFAULT NULL, + move_code TEXT DEFAULT NULL, + requested_move_date TIMESTAMP DEFAULT NULL, + date_submitted TIMESTAMP DEFAULT NULL, + branch TEXT DEFAULT NULL, + origin_duty_location TEXT DEFAULT NULL, + counseling_office TEXT DEFAULT NULL, + too_assigned_user TEXT DEFAULT NULL, + page INTEGER DEFAULT 1, + per_page INTEGER DEFAULT 20 ) RETURNS TABLE ( - move_id UUID, + id UUID, locator TEXT, + submitted_at TIMESTAMP WITH TIME ZONE, orders_id UUID, - available_to_prime_at TIMESTAMP WITH TIME ZONE, - show BOOLEAN, + status TEXT, + locked_by UUID, + too_assigned_id UUID, + counseling_transportation_office_id UUID, + orders JSONB, + mto_shipments JSONB, + counseling_transportation_office JSONB, + too_assigned JSONB, total_count BIGINT ) AS $$ DECLARE sql_query TEXT; offset_value INTEGER; BEGIN + IF page < 1 THEN + page := 1; + END IF; + + IF per_page < 1 THEN + per_page := 20; + END IF; + -- OFFSET for pagination offset_value := (page - 1) * per_page; sql_query := ' - SELECT moves.id AS move_id, - moves.locator::TEXT AS locator, - moves.orders_id, - moves.available_to_prime_at, - moves.show, - COUNT(*) OVER() AS total_count + SELECT + moves.id AS id, + moves.locator::TEXT AS locator, + moves.submitted_at::TIMESTAMP WITH TIME ZONE AS submitted_at, + moves.orders_id AS orders_id, + moves.status::TEXT AS status, + moves.locked_by AS locked_by, + moves.too_assigned_id AS too_assigned_id, + moves.counseling_transportation_office_id AS counseling_transportation_office_id, + json_build_object( + ''id'', orders.id, + ''origin_duty_location_gbloc'', orders.gbloc, + ''service_member'', json_build_object( + ''id'', service_members.id, + ''first_name'', service_members.first_name, + ''last_name'', service_members.last_name, + ''edipi'', service_members.edipi, + ''emplid'', service_members.emplid, + ''affiliation'', service_members.affiliation + ), + ''origin_duty_location'', json_build_object( + ''name'', origin_duty_locations.name + ) + )::JSONB AS orders, + COALESCE( + json_agg( + json_build_object( + ''id'', mto_shipments.id, + ''shipment_type'', mto_shipments.shipment_type, + ''status'', mto_shipments.status, + ''requested_pickup_date'', TO_CHAR(mto_shipments.requested_pickup_date, ''YYYY-MM-DD"T00:00:00Z"''), + ''scheduled_pickup_date'', TO_CHAR(mto_shipments.scheduled_pickup_date, ''YYYY-MM-DD"T00:00:00Z"''), + ''requested_delivery_date'', TO_CHAR(mto_shipments.requested_delivery_date, ''YYYY-MM-DD"T00:00:00Z"''), + ''approved_date'', TO_CHAR(mto_shipments.approved_date, ''YYYY-MM-DD"T00:00:00Z"''), + ''prime_estimated_weight'', mto_shipments.prime_estimated_weight + ) + ) FILTER (WHERE mto_shipments.id IS NOT NULL), + ''[]'' + )::JSONB AS mto_shipments, + json_build_object( + ''name'', counseling_offices.name + )::JSONB AS counseling_transportation_office, + json_build_object( + ''first_name'', too_user.first_name, + ''last_name'', too_user.last_name + )::JSONB AS too_assigned, + COUNT(*) OVER() AS total_count FROM moves INNER JOIN orders ON moves.orders_id = orders.id - INNER JOIN service_members ON orders.service_member_id = service_members.id + LEFT JOIN mto_shipments ON mto_shipments.move_id = moves.id + LEFT JOIN ppm_shipments ON ppm_shipments.shipment_id = mto_shipments.id + LEFT JOIN mto_service_items ON mto_shipments.id = mto_service_items.mto_shipment_id + LEFT JOIN re_services ON mto_service_items.re_service_id = re_services.id + LEFT JOIN service_members ON orders.service_member_id = service_members.id LEFT JOIN duty_locations AS new_duty_locations ON orders.new_duty_location_id = new_duty_locations.id LEFT JOIN duty_locations AS origin_duty_locations ON orders.origin_duty_location_id = origin_duty_locations.id - LEFT JOIN office_users ON moves.too_assigned_id = office_users.id + LEFT JOIN office_users AS too_user ON moves.too_assigned_id = too_user.id + LEFT JOIN office_users AS locked_user ON moves.locked_by = locked_user.id LEFT JOIN transportation_offices AS counseling_offices ON moves.counseling_transportation_office_id = counseling_offices.id - WHERE moves.available_to_prime_at IS NOT NULL - AND moves.show = TRUE + LEFT JOIN shipment_address_updates ON shipment_address_updates.shipment_id = mto_shipments.id + LEFT JOIN move_to_gbloc ON move_to_gbloc.move_id = moves.id + WHERE moves.show = TRUE '; - -- add dynamic filters based on provided parameters - IF move_code IS NOT NULL THEN - sql_query := sql_query || ' AND moves.locator ILIKE ''%' || UPPER(move_code) || '%'' '; - END IF; + -- adding conditionals for destination queue + -- we only want to see moves that have destination requests (shipment address updates, destination service items in SUBMITTED status) + sql_query := sql_query || ' + AND ( + shipment_address_updates.status = ''REQUESTED'' + OR ( + mto_service_items.status = ''SUBMITTED'' + AND re_services.code IN (''DDFSIT'', ''DDASIT'', ''DDDSIT'', ''DDSHUT'', ''DDSFSC'', ''IDFSIT'', ''IDASIT'', ''IDDSIT'', ''IDSHUT'') + ) + ) + '; - IF input_move_id IS NOT NULL THEN - sql_query := sql_query || ' AND moves.id = ''' || input_move_id || ''' '; + -- this should always be passed in from the service object, but will nil check it anyway + IF user_gbloc IS NOT NULL THEN + sql_query := sql_query || ' AND move_to_gbloc.gbloc = ''%' || user_gbloc || '%'' '; END IF; - IF branch IS NOT NULL THEN - sql_query := sql_query || ' AND service_members.affiliation ILIKE ''%' || branch || '%'' '; + IF customer_name IS NOT NULL AND customer_name <> '' THEN + sql_query := sql_query || ' AND ( + service_members.first_name || '' '' || service_members.last_name ILIKE ''%' || customer_name || '%'' + OR service_members.last_name || '' '' || service_members.first_name ILIKE ''%' || customer_name || '%'' + ) '; END IF; IF edipi IS NOT NULL THEN @@ -69,38 +144,69 @@ BEGIN sql_query := sql_query || ' AND service_members.emplid ILIKE ''%' || emplid || '%'' '; END IF; - IF customer_name IS NOT NULL THEN - sql_query := sql_query || ' AND (service_members.first_name || '' '' || service_members.last_name) ILIKE ''%' || customer_name || '%'' '; + IF m_status IS NOT NULL THEN + sql_query := sql_query || ' AND moves.status IN (SELECT unnest($1)) '; END IF; - IF destination_duty_location IS NOT NULL THEN - sql_query := sql_query || ' AND new_duty_locations.name ILIKE ''%' || destination_duty_location || '%'' '; + IF move_code IS NOT NULL THEN + sql_query := sql_query || ' AND moves.locator ILIKE ''%' || UPPER(move_code) || '%'' '; END IF; - IF origin_duty_location IS NOT NULL THEN - sql_query := sql_query || ' AND origin_duty_locations.name ILIKE ''%' || origin_duty_location || '%'' '; + IF requested_move_date IS NOT NULL THEN + sql_query := sql_query || ' AND ( + mto_shipments.requested_pickup_date::DATE = ' || quote_literal(requested_move_date) || '::DATE + OR ppm_shipments.expected_departure_date::DATE = ' || quote_literal(requested_move_date) || '::DATE + OR (mto_shipments.shipment_type = ''HHG_OUTOF_NTS'' AND mto_shipments.requested_delivery_date::DATE = ' || quote_literal(requested_move_date) || '::DATE) + )'; END IF; - IF origin_gbloc IS NOT NULL THEN - sql_query := sql_query || ' AND counseling_offices.gbloc ILIKE ''%' || origin_gbloc || '%'' '; + IF date_submitted IS NOT NULL THEN + sql_query := sql_query || ' AND moves.submitted_at = ' || quote_literal(date_submitted) || ' '; END IF; - IF submitted_at IS NOT NULL THEN - sql_query := sql_query || ' AND moves.submitted_at = ''' || submitted_at || ''' '; + IF branch IS NOT NULL THEN + sql_query := sql_query || ' AND service_members.affiliation ILIKE ''%' || branch || '%'' '; END IF; - IF appeared_in_too_at IS NOT NULL THEN - sql_query := sql_query || ' AND moves.appeared_in_too_at = ''' || appeared_in_too_at || ''' '; + IF origin_duty_location IS NOT NULL AND origin_duty_location <> '' THEN + sql_query := sql_query || ' AND origin_duty_locations.name ILIKE ''%' || origin_duty_location || '%'' '; + END IF; + + IF counseling_office IS NOT NULL THEN + sql_query := sql_query || ' AND counseling_offices.name ILIKE ''%' || counseling_office || '%'' '; END IF; IF too_assigned_user IS NOT NULL THEN - sql_query := sql_query || ' AND (office_users.first_name || '' '' || office_users.last_name) ILIKE ''%' || too_assigned_user || '%'' '; + sql_query := sql_query || ' AND (too_user.first_name || '' '' || too_user.last_name) ILIKE ''%' || quote_literal(too_assigned_user) || '%'' '; END IF; + sql_query := sql_query || ' + GROUP BY + moves.id, + moves.locator, + moves.submitted_at, + moves.orders_id, + moves.status, + moves.locked_by, + moves.too_assigned_id, + moves.counseling_transportation_office_id, + orders.id, + service_members.id, + service_members.first_name, + service_members.last_name, + service_members.edipi, + service_members.emplid, + service_members.affiliation, + origin_duty_locations.name, + counseling_offices.name, + too_user.first_name, + too_user.last_name'; sql_query := sql_query || ' ORDER BY moves.id ASC '; sql_query := sql_query || ' LIMIT ' || per_page || ' OFFSET ' || offset_value || ' '; - RETURN QUERY EXECUTE sql_query; + RAISE NOTICE 'Query: %', sql_query; + + RETURN QUERY EXECUTE sql_query USING m_status; END; -$$ LANGUAGE plpgsql; \ No newline at end of file +$$ LANGUAGE plpgsql; diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go index 878032b252f..cdc4fced512 100644 --- a/pkg/gen/ghcapi/embedded_spec.go +++ b/pkg/gen/ghcapi/embedded_spec.go @@ -4787,6 +4787,8 @@ func init() { "type": "array", "items": { "enum": [ + "SUBMITTED", + "SERVICE COUNSELING COMPLETED", "APPROVALS REQUESTED" ], "type": "string" @@ -21607,6 +21609,8 @@ func init() { "type": "array", "items": { "enum": [ + "SUBMITTED", + "SERVICE COUNSELING COMPLETED", "APPROVALS REQUESTED" ], "type": "string" diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_parameters.go b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_parameters.go index 69171c50aed..86930ef5fab 100644 --- a/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_parameters.go +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_parameters.go @@ -600,7 +600,7 @@ func (o *GetDestinationRequestsQueueParams) bindStatus(rawData []string, hasKey for i, statusIV := range statusIC { statusI := statusIV - if err := validate.EnumCase(fmt.Sprintf("%s.%v", "status", i), "query", statusI, []interface{}{"APPROVALS REQUESTED"}, true); err != nil { + if err := validate.EnumCase(fmt.Sprintf("%s.%v", "status", i), "query", statusI, []interface{}{"SUBMITTED", "SERVICE COUNSELING COMPLETED", "APPROVALS REQUESTED"}, true); err != nil { return err } diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index 7c5b503c83a..fd6dec78247 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -54,7 +54,7 @@ func OfficeUser(officeUser *models.OfficeUser) *ghcmessages.LockedOfficeUser { } func AssignedOfficeUser(officeUser *models.OfficeUser) *ghcmessages.AssignedOfficeUser { - if officeUser != nil { + if officeUser != nil && officeUser.FirstName != "" && officeUser.LastName != "" { payload := ghcmessages.AssignedOfficeUser{ OfficeUserID: strfmt.UUID(officeUser.ID.String()), FirstName: officeUser.FirstName, diff --git a/pkg/models/move.go b/pkg/models/move.go index f0b3ac49269..a8e9a02e8c1 100644 --- a/pkg/models/move.go +++ b/pkg/models/move.go @@ -71,7 +71,7 @@ type Move struct { PPMType *string `db:"ppm_type"` MTOServiceItems MTOServiceItems `has_many:"mto_service_items" fk_id:"move_id"` PaymentRequests PaymentRequests `has_many:"payment_requests" fk_id:"move_id"` - MTOShipments MTOShipments `has_many:"mto_shipments" fk_id:"move_id"` + MTOShipments MTOShipments `json:"mto_shipments" has_many:"mto_shipments" fk_id:"move_id"` ReferenceID *string `db:"reference_id"` ServiceCounselingCompletedAt *time.Time `db:"service_counseling_completed_at"` PrimeCounselingCompletedAt *time.Time `db:"prime_counseling_completed_at"` @@ -98,11 +98,11 @@ type Move struct { SCAssignedID *uuid.UUID `json:"sc_assigned_id" db:"sc_assigned_id"` SCAssignedUser *OfficeUser `belongs_to:"office_users" fk_id:"sc_assigned_id"` TOOAssignedID *uuid.UUID `json:"too_assigned_id" db:"too_assigned_id"` - TOOAssignedUser *OfficeUser `belongs_to:"office_users" fk_id:"too_assigned_id"` + TOOAssignedUser *OfficeUser `json:"too_assigned" belongs_to:"office_users" fk_id:"too_assigned_id"` TIOAssignedID *uuid.UUID `json:"tio_assigned_id" db:"tio_assigned_id"` TIOAssignedUser *OfficeUser `belongs_to:"office_users" fk_id:"tio_assigned_id"` CounselingOfficeID *uuid.UUID `json:"counseling_transportation_office_id" db:"counseling_transportation_office_id"` - CounselingOffice *TransportationOffice `belongs_to:"transportation_offices" fk_id:"counseling_transportation_office_id"` + CounselingOffice *TransportationOffice `json:"counseling_transportation_office" belongs_to:"transportation_offices" fk_id:"counseling_transportation_office_id"` } type MoveWithEarliestDate struct { diff --git a/pkg/models/mto_shipments.go b/pkg/models/mto_shipments.go index 917e75b7978..1b2e6abbc0d 100644 --- a/pkg/models/mto_shipments.go +++ b/pkg/models/mto_shipments.go @@ -101,74 +101,74 @@ const ( // MTOShipment is an object representing data for a move task order shipment type MTOShipment struct { - ID uuid.UUID `db:"id"` - MoveTaskOrder Move `belongs_to:"moves" fk_id:"move_id"` - MoveTaskOrderID uuid.UUID `db:"move_id"` - ScheduledPickupDate *time.Time `db:"scheduled_pickup_date"` - RequestedPickupDate *time.Time `db:"requested_pickup_date"` - RequestedDeliveryDate *time.Time `db:"requested_delivery_date"` - ApprovedDate *time.Time `db:"approved_date"` - FirstAvailableDeliveryDate *time.Time `db:"first_available_delivery_date"` - ActualPickupDate *time.Time `db:"actual_pickup_date"` - RequiredDeliveryDate *time.Time `db:"required_delivery_date"` - ScheduledDeliveryDate *time.Time `db:"scheduled_delivery_date"` - ActualDeliveryDate *time.Time `db:"actual_delivery_date"` - CustomerRemarks *string `db:"customer_remarks"` - CounselorRemarks *string `db:"counselor_remarks"` - PickupAddress *Address `belongs_to:"addresses" fk_id:"pickup_address_id"` - PickupAddressID *uuid.UUID `db:"pickup_address_id"` - DestinationAddress *Address `belongs_to:"addresses" fk_id:"destination_address_id"` - DestinationAddressID *uuid.UUID `db:"destination_address_id"` - DestinationType *DestinationType `db:"destination_address_type"` - MTOAgents MTOAgents `has_many:"mto_agents" fk_id:"mto_shipment_id"` - MTOServiceItems MTOServiceItems `has_many:"mto_service_items" fk_id:"mto_shipment_id"` - SecondaryPickupAddress *Address `belongs_to:"addresses" fk_id:"secondary_pickup_address_id"` - SecondaryPickupAddressID *uuid.UUID `db:"secondary_pickup_address_id"` - HasSecondaryPickupAddress *bool `db:"has_secondary_pickup_address"` - SecondaryDeliveryAddress *Address `belongs_to:"addresses" fk_id:"secondary_delivery_address_id"` - SecondaryDeliveryAddressID *uuid.UUID `db:"secondary_delivery_address_id"` - HasSecondaryDeliveryAddress *bool `db:"has_secondary_delivery_address"` - TertiaryPickupAddress *Address `belongs_to:"addresses" fk_id:"tertiary_pickup_address_id"` - TertiaryPickupAddressID *uuid.UUID `db:"tertiary_pickup_address_id"` - HasTertiaryPickupAddress *bool `db:"has_tertiary_pickup_address"` - TertiaryDeliveryAddress *Address `belongs_to:"addresses" fk_id:"tertiary_delivery_address_id"` - TertiaryDeliveryAddressID *uuid.UUID `db:"tertiary_delivery_address_id"` - HasTertiaryDeliveryAddress *bool `db:"has_tertiary_delivery_address"` - SITDaysAllowance *int `db:"sit_days_allowance"` - SITDurationUpdates SITDurationUpdates `has_many:"sit_extensions" fk_id:"mto_shipment_id"` - PrimeEstimatedWeight *unit.Pound `db:"prime_estimated_weight"` - PrimeEstimatedWeightRecordedDate *time.Time `db:"prime_estimated_weight_recorded_date"` - PrimeActualWeight *unit.Pound `db:"prime_actual_weight"` - BillableWeightCap *unit.Pound `db:"billable_weight_cap"` - BillableWeightJustification *string `db:"billable_weight_justification"` - NTSRecordedWeight *unit.Pound `db:"nts_recorded_weight"` - ShipmentType MTOShipmentType `db:"shipment_type"` - Status MTOShipmentStatus `db:"status"` - Diversion bool `db:"diversion"` - DiversionReason *string `db:"diversion_reason"` - DivertedFromShipmentID *uuid.UUID `db:"diverted_from_shipment_id"` - ActualProGearWeight *unit.Pound `db:"actual_pro_gear_weight"` - ActualSpouseProGearWeight *unit.Pound `db:"actual_spouse_pro_gear_weight"` - RejectionReason *string `db:"rejection_reason"` - Distance *unit.Miles `db:"distance"` - Reweigh *Reweigh `has_one:"reweighs" fk_id:"shipment_id"` - UsesExternalVendor bool `db:"uses_external_vendor"` - StorageFacility *StorageFacility `belongs_to:"storage_facilities" fk:"storage_facility_id"` - StorageFacilityID *uuid.UUID `db:"storage_facility_id"` - ServiceOrderNumber *string `db:"service_order_number"` - TACType *LOAType `db:"tac_type"` - SACType *LOAType `db:"sac_type"` - PPMShipment *PPMShipment `has_one:"ppm_shipment" fk_id:"shipment_id"` - BoatShipment *BoatShipment `has_one:"boat_shipment" fk_id:"shipment_id"` - DeliveryAddressUpdate *ShipmentAddressUpdate `has_one:"shipment_address_update" fk_id:"shipment_id"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` - DeletedAt *time.Time `db:"deleted_at"` - ShipmentLocator *string `db:"shipment_locator"` - OriginSITAuthEndDate *time.Time `db:"origin_sit_auth_end_date"` - DestinationSITAuthEndDate *time.Time `db:"dest_sit_auth_end_date"` - MobileHome *MobileHome `has_one:"mobile_home" fk_id:"shipment_id"` - MarketCode MarketCode `db:"market_code"` + ID uuid.UUID `json:"id" db:"id"` + MoveTaskOrder Move `json:"move_task_order" belongs_to:"moves" fk_id:"move_id"` + MoveTaskOrderID uuid.UUID `json:"move_task_order_id" db:"move_id"` + ScheduledPickupDate *time.Time `json:"scheduled_pickup_date" db:"scheduled_pickup_date"` + RequestedPickupDate *time.Time `json:"requested_pickup_date" db:"requested_pickup_date"` + RequestedDeliveryDate *time.Time `json:"requested_delivery_date" db:"requested_delivery_date"` + ApprovedDate *time.Time `json:"approved_date" db:"approved_date"` + FirstAvailableDeliveryDate *time.Time `json:"first_available_delivery_date" db:"first_available_delivery_date"` + ActualPickupDate *time.Time `json:"actual_pickup_date" db:"actual_pickup_date"` + RequiredDeliveryDate *time.Time `json:"required_delivery_date" db:"required_delivery_date"` + ScheduledDeliveryDate *time.Time `json:"scheduled_delivery_date" db:"scheduled_delivery_date"` + ActualDeliveryDate *time.Time `json:"actual_delivery_date" db:"actual_delivery_date"` + CustomerRemarks *string `json:"customer_remarks" db:"customer_remarks"` + CounselorRemarks *string `json:"counselor_remarks" db:"counselor_remarks"` + PickupAddress *Address `json:"pickup_address" belongs_to:"addresses" fk_id:"pickup_address_id"` + PickupAddressID *uuid.UUID `json:"pickup_address_id" db:"pickup_address_id"` + DestinationAddress *Address `json:"destination_address" belongs_to:"addresses" fk_id:"destination_address_id"` + DestinationAddressID *uuid.UUID `json:"destination_address_id" db:"destination_address_id"` + DestinationType *DestinationType `json:"destination_type" db:"destination_address_type"` + MTOAgents MTOAgents `json:"mto_agents" has_many:"mto_agents" fk_id:"mto_shipment_id"` + MTOServiceItems MTOServiceItems `json:"mto_service_items" has_many:"mto_service_items" fk_id:"mto_shipment_id"` + SecondaryPickupAddress *Address `json:"secondary_pickup_address" belongs_to:"addresses" fk_id:"secondary_pickup_address_id"` + SecondaryPickupAddressID *uuid.UUID `json:"secondary_pickup_address_id" db:"secondary_pickup_address_id"` + HasSecondaryPickupAddress *bool `json:"has_secondary_pickup_address" db:"has_secondary_pickup_address"` + SecondaryDeliveryAddress *Address `json:"secondary_delivery_address" belongs_to:"addresses" fk_id:"secondary_delivery_address_id"` + SecondaryDeliveryAddressID *uuid.UUID `json:"secondary_delivery_address_id" db:"secondary_delivery_address_id"` + HasSecondaryDeliveryAddress *bool `json:"has_secondary_delivery_address" db:"has_secondary_delivery_address"` + TertiaryPickupAddress *Address `json:"tertiary_pickup_address" belongs_to:"addresses" fk_id:"tertiary_pickup_address_id"` + TertiaryPickupAddressID *uuid.UUID `json:"tertiary_pickup_address_id" db:"tertiary_pickup_address_id"` + HasTertiaryPickupAddress *bool `json:"has_tertiary_pickup_address" db:"has_tertiary_pickup_address"` + TertiaryDeliveryAddress *Address `json:"tertiary_delivery_address" belongs_to:"addresses" fk_id:"tertiary_delivery_address_id"` + TertiaryDeliveryAddressID *uuid.UUID `json:"tertiary_delivery_address_id" db:"tertiary_delivery_address_id"` + HasTertiaryDeliveryAddress *bool `json:"has_tertiary_delivery_address" db:"has_tertiary_delivery_address"` + SITDaysAllowance *int `json:"sit_days_allowance" db:"sit_days_allowance"` + SITDurationUpdates SITDurationUpdates `json:"sit_duration_updates" has_many:"sit_extensions" fk_id:"mto_shipment_id"` + PrimeEstimatedWeight *unit.Pound `json:"prime_estimated_weight" db:"prime_estimated_weight"` + PrimeEstimatedWeightRecordedDate *time.Time `json:"prime_estimated_weight_recorded_date" db:"prime_estimated_weight_recorded_date"` + PrimeActualWeight *unit.Pound `json:"prime_actual_weight" db:"prime_actual_weight"` + BillableWeightCap *unit.Pound `json:"billable_weight_cap" db:"billable_weight_cap"` + BillableWeightJustification *string `json:"billable_weight_justification" db:"billable_weight_justification"` + NTSRecordedWeight *unit.Pound `json:"nts_recorded_weight" db:"nts_recorded_weight"` + ShipmentType MTOShipmentType `json:"shipment_type" db:"shipment_type"` + Status MTOShipmentStatus `json:"status" db:"status"` + Diversion bool `json:"diversion" db:"diversion"` + DiversionReason *string `json:"diversion_reason" db:"diversion_reason"` + DivertedFromShipmentID *uuid.UUID `json:"diverted_from_shipment_id" db:"diverted_from_shipment_id"` + ActualProGearWeight *unit.Pound `json:"actual_pro_gear_weight" db:"actual_pro_gear_weight"` + ActualSpouseProGearWeight *unit.Pound `json:"actual_spouse_pro_gear_weight" db:"actual_spouse_pro_gear_weight"` + RejectionReason *string `json:"rejection_reason" db:"rejection_reason"` + Distance *unit.Miles `json:"distance" db:"distance"` + Reweigh *Reweigh `json:"reweigh" has_one:"reweighs" fk_id:"shipment_id"` + UsesExternalVendor bool `json:"uses_external_vendor" db:"uses_external_vendor"` + StorageFacility *StorageFacility `json:"storage_facility" belongs_to:"storage_facilities" fk:"storage_facility_id"` + StorageFacilityID *uuid.UUID `json:"storage_facility_id" db:"storage_facility_id"` + ServiceOrderNumber *string `json:"service_order_number" db:"service_order_number"` + TACType *LOAType `json:"tac_type" db:"tac_type"` + SACType *LOAType `json:"sac_type" db:"sac_type"` + PPMShipment *PPMShipment `json:"ppm_shipment" has_one:"ppm_shipment" fk_id:"shipment_id"` + BoatShipment *BoatShipment `json:"boat_shipment" has_one:"boat_shipment" fk_id:"shipment_id"` + DeliveryAddressUpdate *ShipmentAddressUpdate `json:"delivery_address_update" has_one:"shipment_address_update" fk_id:"shipment_id"` + CreatedAt time.Time `json:"created_at" db:"created_at"` + UpdatedAt time.Time `json:"updated_at" db:"updated_at"` + DeletedAt *time.Time `json:"deleted_at" db:"deleted_at"` + ShipmentLocator *string `json:"shipment_locator" db:"shipment_locator"` + OriginSITAuthEndDate *time.Time `json:"origin_sit_auth_end_date" db:"origin_sit_auth_end_date"` + DestinationSITAuthEndDate *time.Time `json:"destination_sit_auth_end_date" db:"dest_sit_auth_end_date"` + MobileHome *MobileHome `json:"mobile_home" has_one:"mobile_home" fk_id:"shipment_id"` + MarketCode MarketCode `json:"market_code" db:"market_code"` } // TableName overrides the table name used by Pop. diff --git a/pkg/models/order.go b/pkg/models/order.go index 84d5c215349..1ba20fc038d 100644 --- a/pkg/models/order.go +++ b/pkg/models/order.go @@ -67,14 +67,14 @@ type Order struct { CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` ServiceMemberID uuid.UUID `json:"service_member_id" db:"service_member_id"` - ServiceMember ServiceMember `belongs_to:"service_members" fk_id:"service_member_id"` + ServiceMember ServiceMember `json:"service_member" belongs_to:"service_members" fk_id:"service_member_id"` IssueDate time.Time `json:"issue_date" db:"issue_date"` ReportByDate time.Time `json:"report_by_date" db:"report_by_date"` OrdersType internalmessages.OrdersType `json:"orders_type" db:"orders_type"` OrdersTypeDetail *internalmessages.OrdersTypeDetail `json:"orders_type_detail" db:"orders_type_detail"` HasDependents bool `json:"has_dependents" db:"has_dependents"` SpouseHasProGear bool `json:"spouse_has_pro_gear" db:"spouse_has_pro_gear"` - OriginDutyLocation *DutyLocation `belongs_to:"duty_locations" fk_id:"origin_duty_location_id"` + OriginDutyLocation *DutyLocation `json:"origin_duty_location" belongs_to:"duty_locations" fk_id:"origin_duty_location_id"` OriginDutyLocationID *uuid.UUID `json:"origin_duty_location_id" db:"origin_duty_location_id"` NewDutyLocationID uuid.UUID `json:"new_duty_location_id" db:"new_duty_location_id"` NewDutyLocation DutyLocation `belongs_to:"duty_locations" fk_id:"new_duty_location_id"` diff --git a/pkg/services/order/order_fetcher.go b/pkg/services/order/order_fetcher.go index cfbcebfec0d..b08329d3fef 100644 --- a/pkg/services/order/order_fetcher.go +++ b/pkg/services/order/order_fetcher.go @@ -2,6 +2,7 @@ package order import ( "database/sql" + "encoding/json" "fmt" "regexp" "strings" @@ -9,6 +10,8 @@ import ( "github.com/gobuffalo/pop/v6" "github.com/gofrs/uuid" + "github.com/jinzhu/copier" + "github.com/lib/pq" "go.uber.org/zap" "github.com/transcom/mymove/pkg/appcontext" @@ -306,9 +309,31 @@ func (f orderFetcher) ListOrders(appCtx appcontext.AppContext, officeUserID uuid return moves, count, nil } +type MoveWithCount struct { + models.Move + OrdersRaw json.RawMessage `json:"orders" db:"orders"` + Orders *models.Order `json:"-"` + MTOShipmentsRaw json.RawMessage `json:"mto_shipments" db:"mto_shipments"` + MTOShipments *models.MTOShipments `json:"-"` + CounselingOfficeRaw json.RawMessage `json:"counseling_transportation_office" db:"counseling_transportation_office"` + CounselingOffice *models.TransportationOffice `json:"-"` + TOOAssignedRaw json.RawMessage `json:"too_assigned" db:"too_assigned"` + TOOAssignedUser *models.OfficeUser `json:"-"` + TotalCount int64 `json:"total_count" db:"total_count"` +} + +type JSONB []byte + +func (j *JSONB) UnmarshalJSON(data []byte) error { + *j = data + return nil +} + func (f orderFetcher) ListDestinationRequestsOrders(appCtx appcontext.AppContext, officeUserID uuid.UUID, role roles.RoleType, params *services.ListOrderParams) ([]models.Move, int, error) { var moves []models.Move + var movesWithCount []MoveWithCount + // getting the office user's GBLOC var officeUserGbloc string if params.ViewAsGBLOC != nil { officeUserGbloc = *params.ViewAsGBLOC @@ -321,136 +346,81 @@ func (f orderFetcher) ListDestinationRequestsOrders(appCtx appcontext.AppContext } } - ppmCloseoutGblocs := officeUserGbloc == "NAVY" || officeUserGbloc == "TVCB" || officeUserGbloc == "USCG" + // calling the database function with all passed in parameters + err := appCtx.DB().RawQuery("SELECT * FROM get_destination_queue($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + officeUserGbloc, + params.CustomerName, + params.Edipi, + params.Emplid, + pq.Array(params.Status), + params.Locator, + params.RequestedMoveDate, + params.SubmittedAt, + params.Branch, + strings.Join(params.OriginDutyLocation, " "), + params.CounselingOffice, + params.TOOAssignedUser, + params.Page, + params.PerPage). + All(&movesWithCount) - branchQuery := branchFilter(params.Branch, false, ppmCloseoutGblocs) + if err != nil { + return []models.Move{}, 0, err + } - // If the user is associated with the USMC GBLOC we want to show them ALL the USMC moves, so let's override here. - // We also only want to do the gbloc filtering thing if we aren't a USMC user, which we cover with the else. - // var gblocQuery QueryOption - var gblocToFilterBy *string - if officeUserGbloc == "USMC" { - branchQuery = branchFilter(models.StringPointer(string(models.AffiliationMARINES)), false, ppmCloseoutGblocs) - gblocToFilterBy = params.OriginGBLOC + // each row is sent back with the total count, so we will take the value from the first one + var count int64 + if len(movesWithCount) > 0 { + count = movesWithCount[0].TotalCount } else { - gblocToFilterBy = &officeUserGbloc + count = 0 } - gblocQuery := gblocFilterForTOO(gblocToFilterBy) - - locatorQuery := locatorFilter(params.Locator) - dodIDQuery := dodIDFilter(params.Edipi) - emplidQuery := emplidFilter(params.Emplid) - customerNameQuery := customerNameFilter(params.CustomerName) - originDutyLocationQuery := originDutyLocationFilter(params.OriginDutyLocation) - destinationDutyLocationQuery := destinationDutyLocationFilter(params.DestinationDutyLocation) - moveStatusQuery := moveStatusFilter(params.Status) - submittedAtQuery := submittedAtFilter(params.SubmittedAt) - appearedInTOOAtQuery := appearedInTOOAtFilter(params.AppearedInTOOAt) - requestedMoveDateQuery := requestedMoveDateFilter(params.RequestedMoveDate) - closeoutInitiatedQuery := closeoutInitiatedFilter(params.CloseoutInitiated) - closeoutLocationQuery := closeoutLocationFilter(params.CloseoutLocation, ppmCloseoutGblocs) - ppmTypeQuery := ppmTypeFilter(params.PPMType) - ppmStatusQuery := ppmStatusFilter(params.PPMStatus) - scAssignedUserQuery := scAssignedUserFilter(params.SCAssignedUser) - tooAssignedUserQuery := tooAssignedUserFilter(params.TOOAssignedUser) - sortOrderQuery := sortOrder(params.Sort, params.Order, ppmCloseoutGblocs) - counselingQuery := counselingOfficeFilter(params.CounselingOffice) - - // Adding to an array so we can iterate over them and apply the filters after the query structure is set below - options := [20]QueryOption{branchQuery, locatorQuery, dodIDQuery, emplidQuery, customerNameQuery, originDutyLocationQuery, destinationDutyLocationQuery, moveStatusQuery, gblocQuery, submittedAtQuery, appearedInTOOAtQuery, requestedMoveDateQuery, ppmTypeQuery, closeoutInitiatedQuery, closeoutLocationQuery, ppmStatusQuery, sortOrderQuery, scAssignedUserQuery, tooAssignedUserQuery, counselingQuery} - - // we want to set the query up to where it only shows moves that have orders.destination_gbloc that are in the office user's GBLOC (some exclusions apply) - query := appCtx.DB().Q().Scope(utilities.ExcludeDeletedScope(models.MTOShipment{})).EagerPreload( - "Orders.ServiceMember", - "Orders.NewDutyLocation.Address", - "Orders.OriginDutyLocation.Address", - "Orders.Entitlement", - "MTOShipments.DeliveryAddressUpdate", - "MTOServiceItems.ReService.Code", - "ShipmentGBLOC", - "MTOShipments.PPMShipment", - "CloseoutOffice", - "LockedByOfficeUser", - "CounselingOffice", - "SCAssignedUser", - "TOOAssignedUser", - ).InnerJoin("orders", "orders.id = moves.orders_id"). - InnerJoin("service_members", "orders.service_member_id = service_members.id"). - InnerJoin("mto_shipments", "moves.id = mto_shipments.move_id"). - InnerJoin("mto_service_items", "mto_shipments.id = mto_service_items.mto_shipment_id"). - InnerJoin("re_services", "mto_service_items.re_service_id = re_services.id"). - InnerJoin("duty_locations as origin_dl", "orders.origin_duty_location_id = origin_dl.id"). - LeftJoin("transportation_offices as origin_to", "origin_dl.transportation_office_id = origin_to.id"). - LeftJoin("move_to_gbloc", "move_to_gbloc.move_id = moves.id"). - LeftJoin("duty_locations as dest_dl", "dest_dl.id = orders.new_duty_location_id"). - LeftJoin("office_users", "office_users.id = moves.locked_by"). - LeftJoin("transportation_offices", "moves.counseling_transportation_office_id = transportation_offices.id"). - LeftJoin("office_users as assigned_user", "moves.too_assigned_id = assigned_user.id"). - LeftJoin("ppm_shipments", "ppm_shipments.shipment_id = mto_shipments.id"). - LeftJoin("shipment_address_updates", "shipment_address_updates.shipment_id = mto_shipments.id"). - Where("moves.status = 'APPROVALS REQUESTED' "+ - "AND mto_service_items.status = 'SUBMITTED' "+ - "AND re_services.code IN ('DDFSIT', 'DDASIT', 'DDDSIT', 'DDSHUT', 'DDSFSC') "+ - "OR shipment_address_updates.status = 'REQUESTED'"). - Where("orders.destination_gbloc = ?", officeUserGbloc). - Where("moves.show = ?", models.BoolPointer(true)) - - for _, option := range options { - if option != nil { - option(query) + // we have to manually loop through each move and populate the nested objects that the queue uses/needs + for i := range movesWithCount { + // populating Move.Orders struct + var order models.Order + if err := json.Unmarshal(movesWithCount[i].OrdersRaw, &order); err != nil { + return nil, 0, fmt.Errorf("error unmarshaling orders JSON: %w", err) } - } - - // Pass zeros into paginate in this case. Which will give us 1 page and 20 per page respectively - if params.Page == nil { - params.Page = models.Int64Pointer(0) - } - if params.PerPage == nil { - params.PerPage = models.Int64Pointer(0) - } + movesWithCount[i].OrdersRaw = nil + movesWithCount[i].Orders = &order - var groupByColumms []string - groupByColumms = append(groupByColumms, "service_members.id", "orders.id", "origin_dl.id") - - if params.Sort != nil && *params.Sort == "originDutyLocation" { - groupByColumms = append(groupByColumms, "origin_dl.name") - } - if params.Sort != nil && *params.Sort == "destinationDutyLocation" { - groupByColumms = append(groupByColumms, "dest_dl.name") - } - if params.Sort != nil && *params.Sort == "originGBLOC" { - groupByColumms = append(groupByColumms, "origin_to.id") - } - if params.Sort != nil && *params.Sort == "counselingOffice" { - groupByColumms = append(groupByColumms, "transportation_offices.id") - } - if params.Sort != nil && *params.Sort == "assignedTo" { - groupByColumms = append(groupByColumms, "assigned_user.last_name", "assigned_user.first_name") - } - - err := query.GroupBy("moves.id", groupByColumms...).Paginate(int(*params.Page), int(*params.PerPage)).All(&moves) - if err != nil { - return []models.Move{}, 0, err - } + // populating Move.MTOShipments array + var shipments models.MTOShipments + if err := json.Unmarshal(movesWithCount[i].MTOShipmentsRaw, &shipments); err != nil { + return nil, 0, fmt.Errorf("error unmarshaling shipments JSON: %w", err) + } + movesWithCount[i].MTOShipmentsRaw = nil + movesWithCount[i].MTOShipments = &shipments - count := query.Paginator.TotalEntriesSize + // populating Moves.CounselingOffice struct + var counselingTransportationOffice models.TransportationOffice + if err := json.Unmarshal(movesWithCount[i].CounselingOfficeRaw, &counselingTransportationOffice); err != nil { + return nil, 0, fmt.Errorf("error unmarshaling counseling_transportation_office JSON: %w", err) + } + movesWithCount[i].CounselingOfficeRaw = nil + movesWithCount[i].CounselingOffice = &counselingTransportationOffice - for i := range moves { - if moves[i].Orders.OriginDutyLocation != nil { - loadErr := appCtx.DB().Load(moves[i].Orders.OriginDutyLocation, "TransportationOffice") - if loadErr != nil { - return []models.Move{}, 0, err - } + // populating Moves.TOOAssigned struct + var tooAssigned models.OfficeUser + if err := json.Unmarshal(movesWithCount[i].TOOAssignedRaw, &tooAssigned); err != nil { + return nil, 0, fmt.Errorf("error unmarshaling too_assigned JSON: %w", err) } + movesWithCount[i].TOOAssignedRaw = nil + movesWithCount[i].TOOAssignedUser = &tooAssigned + } - err := appCtx.DB().Load(&moves[i].Orders.ServiceMember, "BackupContacts") - if err != nil { - return []models.Move{}, 0, err + // the handler consumes a Move object, so we have to copy our custom struct into the Move struct + for _, moveWithCount := range movesWithCount { + var move models.Move + if err := copier.Copy(&move, &moveWithCount); err != nil { + return nil, 0, fmt.Errorf("error copying movesWithCount into Moves: %w", err) } + moves = append(moves, move) } - return moves, count, nil + return moves, int(count), nil } func (f orderFetcher) ListAllOrderLocations(appCtx appcontext.AppContext, officeUserID uuid.UUID, params *services.ListOrderParams) ([]models.Move, error) { diff --git a/swagger-def/ghc.yaml b/swagger-def/ghc.yaml index 63f6083a887..b2bcc94b3b8 100644 --- a/swagger-def/ghc.yaml +++ b/swagger-def/ghc.yaml @@ -3819,6 +3819,8 @@ paths: items: type: string enum: + - SUBMITTED + - SERVICE COUNSELING COMPLETED - APPROVALS REQUESTED - in: query name: orderType diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml index 6df16f2884f..073a948672d 100644 --- a/swagger/ghc.yaml +++ b/swagger/ghc.yaml @@ -3990,6 +3990,8 @@ paths: items: type: string enum: + - SUBMITTED + - SERVICE COUNSELING COMPLETED - APPROVALS REQUESTED - in: query name: orderType From 74b5d0997531fd67dd57fe6ca053b21d2b64097f Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Mon, 27 Jan 2025 15:00:26 +0000 Subject: [PATCH 03/15] refactoring using USING, need to add USMC logic and refine --- ...73216_add_destination_queue_db_func.up.sql | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql b/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql index f6c40773200..fad30b87d29 100644 --- a/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql +++ b/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql @@ -112,8 +112,7 @@ BEGIN WHERE moves.show = TRUE '; - -- adding conditionals for destination queue - -- we only want to see moves that have destination requests (shipment address updates, destination service items in SUBMITTED status) + -- add destination queue-specific filters (pending dest address requests, dest SIT & shuttle service items) sql_query := sql_query || ' AND ( shipment_address_updates.status = ''REQUESTED'' @@ -124,60 +123,59 @@ BEGIN ) '; - -- this should always be passed in from the service object, but will nil check it anyway IF user_gbloc IS NOT NULL THEN - sql_query := sql_query || ' AND move_to_gbloc.gbloc = ''%' || user_gbloc || '%'' '; + sql_query := sql_query || ' AND move_to_gbloc.gbloc = $1 '; END IF; - IF customer_name IS NOT NULL AND customer_name <> '' THEN + IF customer_name IS NOT NULL THEN sql_query := sql_query || ' AND ( - service_members.first_name || '' '' || service_members.last_name ILIKE ''%' || customer_name || '%'' - OR service_members.last_name || '' '' || service_members.first_name ILIKE ''%' || customer_name || '%'' - ) '; + service_members.first_name || '' '' || service_members.last_name ILIKE ''%'' || $2 || ''%'' + OR service_members.last_name || '' '' || service_members.first_name ILIKE ''%'' || $2 || ''%'' + )'; END IF; IF edipi IS NOT NULL THEN - sql_query := sql_query || ' AND service_members.edipi ILIKE ''%' || edipi || '%'' '; + sql_query := sql_query || ' AND service_members.edipi ILIKE ''%'' || $3 || ''%'' '; END IF; IF emplid IS NOT NULL THEN - sql_query := sql_query || ' AND service_members.emplid ILIKE ''%' || emplid || '%'' '; + sql_query := sql_query || ' AND service_members.emplid ILIKE ''%'' || $4 || ''%'' '; END IF; IF m_status IS NOT NULL THEN - sql_query := sql_query || ' AND moves.status IN (SELECT unnest($1)) '; + sql_query := sql_query || ' AND moves.status = ANY($5) '; END IF; IF move_code IS NOT NULL THEN - sql_query := sql_query || ' AND moves.locator ILIKE ''%' || UPPER(move_code) || '%'' '; + sql_query := sql_query || ' AND moves.locator ILIKE ''%'' || $6 || ''%'' '; END IF; IF requested_move_date IS NOT NULL THEN sql_query := sql_query || ' AND ( - mto_shipments.requested_pickup_date::DATE = ' || quote_literal(requested_move_date) || '::DATE - OR ppm_shipments.expected_departure_date::DATE = ' || quote_literal(requested_move_date) || '::DATE - OR (mto_shipments.shipment_type = ''HHG_OUTOF_NTS'' AND mto_shipments.requested_delivery_date::DATE = ' || quote_literal(requested_move_date) || '::DATE) + mto_shipments.requested_pickup_date::DATE = $7::DATE + OR ppm_shipments.expected_departure_date::DATE = $7::DATE + OR (mto_shipments.shipment_type = ''HHG_OUTOF_NTS'' AND mto_shipments.requested_delivery_date::DATE = $7::DATE) )'; END IF; IF date_submitted IS NOT NULL THEN - sql_query := sql_query || ' AND moves.submitted_at = ' || quote_literal(date_submitted) || ' '; + sql_query := sql_query || ' AND moves.submitted_at::DATE = $8::DATE '; END IF; IF branch IS NOT NULL THEN - sql_query := sql_query || ' AND service_members.affiliation ILIKE ''%' || branch || '%'' '; + sql_query := sql_query || ' AND service_members.affiliation ILIKE ''%'' || $9 || ''%'' '; END IF; - IF origin_duty_location IS NOT NULL AND origin_duty_location <> '' THEN - sql_query := sql_query || ' AND origin_duty_locations.name ILIKE ''%' || origin_duty_location || '%'' '; + IF origin_duty_location IS NOT NULL THEN + sql_query := sql_query || ' AND origin_duty_locations.name ILIKE ''%'' || $10 || ''%'' '; END IF; IF counseling_office IS NOT NULL THEN - sql_query := sql_query || ' AND counseling_offices.name ILIKE ''%' || counseling_office || '%'' '; + sql_query := sql_query || ' AND counseling_offices.name ILIKE ''%'' || $11 || ''%'' '; END IF; IF too_assigned_user IS NOT NULL THEN - sql_query := sql_query || ' AND (too_user.first_name || '' '' || too_user.last_name) ILIKE ''%' || quote_literal(too_assigned_user) || '%'' '; + sql_query := sql_query || ' AND (too_user.first_name || '' '' || too_user.last_name) ILIKE ''%'' || $12 || ''%'' '; END IF; sql_query := sql_query || ' @@ -202,11 +200,11 @@ BEGIN too_user.first_name, too_user.last_name'; sql_query := sql_query || ' ORDER BY moves.id ASC '; - sql_query := sql_query || ' LIMIT ' || per_page || ' OFFSET ' || offset_value || ' '; - - RAISE NOTICE 'Query: %', sql_query; + sql_query := sql_query || ' LIMIT $13 OFFSET $14 '; - RETURN QUERY EXECUTE sql_query USING m_status; + RETURN QUERY EXECUTE sql_query + USING user_gbloc, customer_name, edipi, emplid, m_status, move_code, requested_move_date, date_submitted, + branch, origin_duty_location, counseling_office, too_assigned_user, per_page, offset_value; END; $$ LANGUAGE plpgsql; From 5fb02b3014ae6e032d089bd15ee75fca60ec4950 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Tue, 28 Jan 2025 15:41:55 +0000 Subject: [PATCH 04/15] added move to dest gbloc view --- migrations/app/migrations_manifest.txt | 2 +- ...ation_queue_db_func_and_gbloc_view.up.sql} | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) rename migrations/app/schema/{20250123173216_add_destination_queue_db_func.up.sql => 20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql} (78%) diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index fefc26f6685..c8c62bb5807 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1075,4 +1075,4 @@ 20250113201232_update_estimated_pricing_procs_add_is_peak_func.up.sql 20250116200912_disable_homesafe_stg_cert.up.sql 20250120144247_update_pricing_proc_to_use_110_percent_weight.up.sql -20250123173216_add_destination_queue_db_func.up.sql +20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql diff --git a/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql b/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql similarity index 78% rename from migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql rename to migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql index fad30b87d29..f6d740023d1 100644 --- a/migrations/app/schema/20250123173216_add_destination_queue_db_func.up.sql +++ b/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql @@ -1,3 +1,43 @@ +CREATE OR REPLACE VIEW move_to_dest_gbloc +AS +SELECT distinct move_id, gbloc FROM ( + SELECT sh.move_id, s.affiliation, + COALESCE(pctg.gbloc, coalesce(pctg_oconus_bos.gbloc, coalesce(pctg_oconus.gbloc, pctg_ppm.gbloc))) AS gbloc + FROM mto_shipments sh + JOIN moves m ON sh.move_id = m.id + JOIN orders o on m.orders_id = o.id + JOIN service_members s on o.service_member_id = s.id + LEFT JOIN ( SELECT a.id AS address_id, + pctg_1.gbloc, pctg_1.postal_code + FROM addresses a + JOIN postal_code_to_gblocs pctg_1 ON a.postal_code::text = pctg_1.postal_code::text) pctg ON pctg.address_id = sh.destination_address_id + LEFT JOIN ( SELECT ppm.shipment_id, + pctg_1.gbloc + FROM ppm_shipments ppm + JOIN addresses ppm_address ON ppm.destination_postal_address_id = ppm_address.id + JOIN postal_code_to_gblocs pctg_1 ON ppm_address.postal_code::text = pctg_1.postal_code::text) pctg_ppm ON pctg_ppm.shipment_id = sh.id + LEFT JOIN ( SELECT a.id AS address_id, + cast(jr.code as varchar) AS gbloc, ga.department_indicator + FROM addresses a + JOIN re_oconus_rate_areas ora ON a.us_post_region_cities_id = ora.us_post_region_cities_id + JOIN gbloc_aors ga ON ora.id = ga.oconus_rate_area_id + JOIN jppso_regions jr ON ga.jppso_regions_id = jr.id + ) pctg_oconus_bos ON pctg_oconus_bos.address_id = sh.destination_address_id + and case when s.affiliation = 'AIR_FORCE' THEN 'AIR_AND_SPACE_FORCE' + when s.affiliation = 'SPACE_FORCE' THEN 'AIR_AND_SPACE_FORCE' + when s.affiliation = 'NAVY' THEN 'NAVY_AND_MARINES' + when s.affiliation = 'MARINES' THEN 'NAVY_AND_MARINES' + else s.affiliation + end = pctg_oconus_bos.department_indicator + LEFT JOIN ( SELECT a.id AS address_id, + cast(pctg_1.code as varchar) AS gbloc, ga.department_indicator + FROM addresses a + JOIN re_oconus_rate_areas ora ON a.us_post_region_cities_id = ora.us_post_region_cities_id + JOIN gbloc_aors ga ON ora.id = ga.oconus_rate_area_id + JOIN jppso_regions pctg_1 ON ga.jppso_regions_id = pctg_1.id + ) pctg_oconus ON pctg_oconus.address_id = sh.destination_address_id and pctg_oconus.department_indicator is null + WHERE sh.deleted_at IS NULL) as m; + -- database function that returns a list of moves that have destination requests -- this includes shipment address update requests & destination service items CREATE OR REPLACE FUNCTION get_destination_queue( From 4a2ab0d530f79e3de29a1556753cb70dc52b474e Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Wed, 29 Jan 2025 16:05:52 +0000 Subject: [PATCH 05/15] updating move_to_gbloc to move_to_dest_gbloc --- ...23173216_add_destination_queue_db_func_and_gbloc_view.up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql b/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql index f6d740023d1..bda0e93ab39 100644 --- a/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql +++ b/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql @@ -148,7 +148,7 @@ BEGIN LEFT JOIN transportation_offices AS counseling_offices ON moves.counseling_transportation_office_id = counseling_offices.id LEFT JOIN shipment_address_updates ON shipment_address_updates.shipment_id = mto_shipments.id - LEFT JOIN move_to_gbloc ON move_to_gbloc.move_id = moves.id + LEFT JOIN move_to_dest_gbloc ON move_to_dest_gbloc.move_id = moves.id WHERE moves.show = TRUE '; From c997ab5e0cb5504e003b32cf6cabebb335d1469b Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Fri, 31 Jan 2025 14:15:23 +0000 Subject: [PATCH 06/15] adding things, awaiting db funcs from beth for USMC logic --- ...nation_queue_db_func_and_gbloc_view.up.sql | 261 ++++++++++++++++-- .../ServicesCounselingQueue.jsx | 2 +- 2 files changed, 237 insertions(+), 26 deletions(-) diff --git a/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql b/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql index bda0e93ab39..bd09ad533c4 100644 --- a/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql +++ b/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql @@ -1,3 +1,210 @@ +--insert USMC gbloc +insert into jppso_regions values ('85b324ae-1a0d-4f7d-971f-ea509dcc73d7', 'USMC','USMC',now(),now()); + +--insert USMC AORs +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('a8eb35e2-275e-490b-9945-1971b954b958'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'e418be11-2b6f-4714-b026-e293528c50bd'::uuid,'MARINES',NULL,true,now(),now()), + ('ada1b48d-d2e0-481a-8a2e-a265a824647d'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'b41c5636-96dd-4f0d-a18e-eebb17f97ea5'::uuid,'MARINES',NULL,true,now(),now()), + ('588af482-7cd7-42ea-8e05-49dce645ecbe'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'02f32a6a-0338-4545-8437-059862892d2c'::uuid,'MARINES',NULL,true,now(),now()), + ('1ff3bed7-bbf3-432d-9da3-d76264d72913'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'0f128476-d52a-418c-8ba0-c8bfd1c32629'::uuid,'MARINES',NULL,true,now(),now()), + ('0853a854-98b2-4363-a2b1-db14c44dde2f'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'081f84c3-17ec-4ff6-97ce-d5c44a8e4a28'::uuid,'MARINES',NULL,true,now(),now()), + ('21c1ba40-2533-4196-9eb5-6ffddff3a794'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'ce7cdd91-e323-43a6-a604-daaa6bf8be06'::uuid,'MARINES',NULL,true,now(),now()), + ('19ca4073-8736-453e-bb0d-9b13e3b557b0'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'655050d4-711a-43b7-b06d-828ec990c35e'::uuid,'MARINES',NULL,true,now(),now()), + ('a9c2131e-09c3-480d-ba3e-0c144de18aa5'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'455a34af-30a8-4a98-a62b-6f40fd7f047b'::uuid,'MARINES',NULL,true,now(),now()), + ('649e72f8-cac8-483f-a9ed-c9659e37545b'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'1bf1daee-51bb-4c28-aac8-a126ef596486'::uuid,'MARINES',NULL,true,now(),now()), + ('7173f871-f948-4eed-86ae-5e977b16c426'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'15b1d852-0dde-4e1b-b3c6-e08bbc714db3'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('4f626e2a-cad0-4b4d-baa8-3101275bac23'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'d63de5bb-379b-4f3b-b4c1-554b9746d311'::uuid,'MARINES',NULL,true,now(),now()), + ('de59c604-9119-48fc-bf3f-883de17b7ee6'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'95d142f8-b50a-4108-b5e2-fbd3b7022d3b'::uuid,'MARINES',NULL,true,now(),now()), + ('1cec884c-9e34-42fe-8887-1e8b8fa1cd2e'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'922bb7da-d0e9-431c-a493-95a861dca929'::uuid,'MARINES',NULL,true,now(),now()), + ('0f24b453-e3bc-47ec-86b5-8d8937f65504'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'79aef0ca-9065-4c0e-a134-2889b250cc38'::uuid,'MARINES',NULL,true,now(),now()), + ('2d55560a-7d0a-474f-a0f9-b31c5be8d80e'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'1f181025-e410-41ac-935b-4b5147353f84'::uuid,'MARINES',NULL,true,now(),now()), + ('abcc37f6-9209-4639-8fa1-c8d5f6e4e77d'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'d9ede620-c3f1-4f8d-b60b-eb93664382f7'::uuid,'MARINES',NULL,true,now(),now()), + ('3ac990d2-5df4-4889-a943-2710f818e75a'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'bd27700b-3094-46cc-807b-f18472cbfaf0'::uuid,'MARINES',NULL,true,now(),now()), + ('f3084090-680f-4656-947a-eb2e773e4076'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'2f4a1689-ee65-45fa-9d0c-debd9344f8b9'::uuid,'MARINES',NULL,true,now(),now()), + ('a35ac50b-09a1-46ef-969e-17569717ee10'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'20c52934-2588-4d8f-b3ed-c046d771f4e9'::uuid,'MARINES',NULL,true,now(),now()), + ('107c1479-e6d9-44cb-8342-ac934055074d'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'15fe364b-add5-4ade-bdd9-38fe442616fb'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('843db089-67cb-463d-a255-1d198f4f7aaa'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'e5a1248e-3870-4a4c-9c9d-2056234eb64a'::uuid,'MARINES',NULL,true,now(),now()), + ('ac820ac9-d380-4c11-9103-172795658e1f'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'efc92db1-4b06-49ab-a295-a98f3f6c9c04'::uuid,'MARINES',NULL,true,now(),now()), + ('dca7ccd2-e438-4f82-8b76-9b61fbf2a593'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'13474ce5-8839-4af7-b975-c3f70ccdab7b'::uuid,'MARINES',NULL,true,now(),now()), + ('def1095b-2a5c-4f7c-8889-9fde15a7ec06'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'d3b05c5e-6faa-4fa9-b725-0904f8a4f3d7'::uuid,'MARINES',NULL,true,now(),now()), + ('d5fbc738-ee31-4c51-8fd4-bbb8db941dc1'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'e5f849fe-5672-4d0d-881c-078e56eea33d'::uuid,'MARINES',NULL,true,now(),now()), + ('ac8c75fe-f637-429d-80fa-913321a65372'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'fca7c60e-fbd9-4885-afdb-ae41c521b560'::uuid,'MARINES',NULL,true,now(),now()), + ('c33eb1f8-b0fc-4670-af5e-5bd423eca6e7'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'0d52a0f0-f39c-4d34-9387-2df45a5810d4'::uuid,'MARINES',NULL,true,now(),now()), + ('188ea995-b8b7-4ce0-97a9-f553f3b72c2f'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'af008e75-81d5-4211-8204-4964c78e70d9'::uuid,'MARINES',NULL,true,now(),now()), + ('4f13c1a6-059b-4aa2-9250-4316e60da2a7'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'dd689e55-af29-4c76-b7e0-c2429ea4833c'::uuid,'MARINES',NULL,true,now(),now()), + ('67e1ff6f-b79b-45cf-b250-3d4ec89bebae'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'60e08330-6869-4586-8198-35f7a4ae9ea7'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('3a7c679c-0439-4030-8f7e-f6d8e92720d7'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'f7478e79-dbfe-46c8-b337-1e7c46df79dc'::uuid,'MARINES',NULL,true,now(),now()), + ('db91f133-a301-4c87-af9f-6c10584063e6'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'486d7dd4-f51f-4b13-88fc-830b5b15f0a8'::uuid,'MARINES',NULL,true,now(),now()), + ('6b83cc22-36a9-470e-9f43-e65ade5e8a66'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'f66edf62-ba5e-47f8-8264-b6dc9e7dd9ba'::uuid,'MARINES',NULL,true,now(),now()), + ('3df30221-acd3-4428-890c-3a5ef5296cb1'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'0052c55a-d9d7-46b0-9328-58d3911f61b4'::uuid,'MARINES',NULL,true,now(),now()), + ('bae4c9c6-94d8-4ad0-bfc0-7642f5353199'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'16a51fd1-04ed-432a-a8d7-9f17c1de095d'::uuid,'MARINES',NULL,true,now(),now()), + ('1cd873ff-d170-4f48-8f5b-5c5146052d68'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'64b4756f-437d-4aa5-a95e-c396f0cafcbb'::uuid,'MARINES',NULL,true,now(),now()), + ('eb4d870b-8d66-4135-b294-8992e56ad76f'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'f2de1da5-a737-4493-b3f7-7700944a5b62'::uuid,'MARINES',NULL,true,now(),now()), + ('e8c0709c-1f08-4d3e-b848-72ab5b524677'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'7a9c2adb-0562-42c1-a5f1-81710dd19590'::uuid,'MARINES',NULL,true,now(),now()), + ('3c53df36-ee6b-4bc0-a5ca-cedc1dc3c32e'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'e713eed6-35a6-456d-bfb2-0e0646078ab8'::uuid,'MARINES',NULL,true,now(),now()), + ('7cbc7e6c-4da7-47a1-ac93-171b89dba1e0'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'a4594957-0ec6-4edc-85c2-68871f4f6359'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('d22cb1d2-79b4-45c9-bb6f-8acef54a67b0'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'acd4d2f9-9a49-4a73-be14-3515f19ba0d1'::uuid,'MARINES',NULL,true,now(),now()), + ('5b5e7a5a-d027-44f2-9b4f-f25c1a91bc00'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'c8300ab6-519c-4bef-9b81-25e7334773ca'::uuid,'MARINES',NULL,true,now(),now()), + ('21307477-912d-40ca-a399-6dfebcc322ea'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'67eca2ce-9d88-44ca-bb17-615f3199415c'::uuid,'MARINES',NULL,true,now(),now()), + ('82282efb-7fef-4a6d-a260-a18d2f21fa8d'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'149c3a94-abb1-4af0-aabf-3af019d5e243'::uuid,'MARINES',NULL,true,now(),now()), + ('3bdad313-d414-4842-8e6e-3675e20d78eb'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'87c09b47-058e-47ea-9684-37a8ac1f7120'::uuid,'MARINES',NULL,true,now(),now()), + ('db80b591-045d-4907-9b77-6694fe34e3ed'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'0ba81e85-f175-435a-a7c2-a22d0c44cc7b'::uuid,'MARINES',NULL,true,now(),now()), + ('05970a8a-28aa-454f-a28e-31327a2415dd'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'946256dc-1572-4201-b5ff-464da876f5ff'::uuid,'MARINES',NULL,true,now(),now()), + ('c1b3e0be-0463-4dfc-ab22-016037b41a05'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'5d381518-6beb-422e-8c57-4682b87ff1fe'::uuid,'MARINES',NULL,true,now(),now()), + ('1a92f4b0-4060-4f1b-9b45-b3fc47c5f08d'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'b07fb701-2c9b-4847-a459-76b1f47aa872'::uuid,'MARINES',NULL,true,now(),now()), + ('73794c53-a915-41af-b93d-5ada2e174409'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'b6c51584-f3e8-4c9e-9bdf-f7cac0433319'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('c03994c2-9e2a-4888-a5c2-c81ee05eba31'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'6097d01d-78ef-40d5-8699-7f8a8e48f4e7'::uuid,'MARINES',NULL,true,now(),now()), + ('ae6a55fd-2171-425a-9716-56d3fb9452e3'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'e48a3602-7fdd-4527-a8a7-f244fb331228'::uuid,'MARINES',NULL,true,now(),now()), + ('f052acf4-7d4a-4061-a2f4-ba19ff17ec4d'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'df47ef29-e902-4bd3-a32d-88d6187399b3'::uuid,'MARINES',NULL,true,now(),now()), + ('10b47428-bef7-4cfe-8564-2ccb654d514a'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'0f4eddf9-b727-4725-848e-3d9329553823'::uuid,'MARINES',NULL,true,now(),now()), + ('4a547501-6aae-4886-be5c-e5a0fad05441'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'d16ebe80-2195-48cf-ba61-b5efb8212754'::uuid,'MARINES',NULL,true,now(),now()), + ('d99cbe3a-84a0-4a2c-8e05-41ce066570ea'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'093268d0-597b-40bd-882a-8f385480bc68'::uuid,'MARINES',NULL,true,now(),now()), + ('ad09f13c-9dd9-468b-ab92-ad3e2d77c905'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'edb8b022-9534-44e7-87ea-e93f5451f057'::uuid,'MARINES',NULL,true,now(),now()), + ('362482c6-7770-49f4-86c9-89e2990e5345'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'4c62bb35-4d2a-4995-9c4f-8c665f2f6d3e'::uuid,'MARINES',NULL,true,now(),now()), + ('45994e2f-1aa4-4aa1-8631-a347a4463bc2'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'58ccfcc5-ded6-4f91-8cb7-8687bc56c4c6'::uuid,'MARINES',NULL,true,now(),now()), + ('b4fd2bf2-05b2-4429-afcb-ebbae512fd2b'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'dbc839c6-7b56-45b0-ab36-a0e77b0d538c'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('5aa5f596-a9bb-4f37-af0b-6e0cfbfbc711'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'c77e434a-1bf9-44c6-92aa-d377d72d1d44'::uuid,'MARINES',NULL,true,now(),now()), + ('c7bc79d4-525e-496d-93bd-2ea76b32baf4'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'89533190-e613-4c36-99df-7ee3871bb071'::uuid,'MARINES',NULL,true,now(),now()), + ('5bc99ccb-7010-4d2f-99a0-9277eda982ba'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'c86cb50a-99f6-41ed-8e9d-f096bd5cadca'::uuid,'MARINES',NULL,true,now(),now()), + ('dc97dd55-213b-4212-8c1a-aaee7b92fc55'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'b65bd7c4-6251-4682-aa07-77f761c363af'::uuid,'MARINES',NULL,true,now(),now()), + ('3a4daddc-a377-440f-bb5f-d33024374e3e'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'88df8618-798a-4a12-9b14-a7267a6cfd7f'::uuid,'MARINES',NULL,true,now(),now()), + ('e729c30a-0bec-424a-919a-45cbe31998e9'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'15894c4f-eb1a-4b6b-8b3f-e3abc4eeee8d'::uuid,'MARINES',NULL,true,now(),now()), + ('74b6df4f-08ea-48fb-9628-15c3f47f3a27'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'83fd3eb7-6269-46e2-84e1-f6180f40b9e8'::uuid,'MARINES',NULL,true,now(),now()), + ('643112a7-71a9-41d1-97f6-92aee969478a'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'b0f95e07-e3d1-4403-a402-50be9a542cf9'::uuid,'MARINES',NULL,true,now(),now()), + ('73b35029-077b-4d30-8f5a-34c3785f6e96'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'cef18ff2-8f48-47c0-8062-a40f9dec641c'::uuid,'MARINES',NULL,true,now(),now()), + ('9d8862ec-4358-497a-8154-65a83c676261'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'15eefe71-55ae-40b0-8bb5-f1952fcf45c8'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('8d5591d4-fcc4-44a9-babd-b575672ad6a9'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'d3c5d0a7-0477-44f6-8279-f6b9fa7b3436'::uuid,'MARINES',NULL,true,now(),now()), + ('c1057fc7-1c8b-44e2-a175-131ff0d7429f'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'8b89e7d7-cb36-40bd-ba14-699f1e4b1806'::uuid,'MARINES',NULL,true,now(),now()), + ('ae7949e2-9504-4874-92be-98460e8126da'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'5b833661-8fda-4f2a-9f2b-e1f4c21749a4'::uuid,'MARINES',NULL,true,now(),now()), + ('acf2cacd-0d4f-4de1-afdc-2eb1e72eeb80'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'38bed277-7990-460d-ad27-a69559638f42'::uuid,'MARINES',NULL,true,now(),now()), + ('c66c3d59-def9-4e08-8bee-5b2bacfc5cfd'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'67081e66-7e84-4bac-9c31-aa3e45bbba23'::uuid,'MARINES',NULL,true,now(),now()), + ('1f8cd63a-8dfe-4357-9ac5-2bef71f9d564'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'99e3f894-595f-403a-83f5-f7e035dd1f20'::uuid,'MARINES',NULL,true,now(),now()), + ('3e8030da-8316-4330-abea-35452e39fa61'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'1c0a5988-492f-4bc2-8409-9a143a494248'::uuid,'MARINES',NULL,true,now(),now()), + ('ce3c740a-b0a2-4a55-9abe-c2426fb8d821'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'63e10b54-5348-4362-932e-6613a8db4d42'::uuid,'MARINES',NULL,true,now(),now()), + ('9e913f22-287f-4e42-9544-46d9c6741db7'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'437e9930-0625-4448-938d-ba93a0a98ab5'::uuid,'MARINES',NULL,true,now(),now()), + ('d0ae21fe-07c0-40ee-9aa3-877d6d6a6bb9'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'87d3cb47-1026-4f42-bff6-899ff0fa7660'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('6f8d7c90-7682-41b7-b5cc-9d4aeb2e22ef'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'0115586e-be8c-4808-b65a-d417fad19238'::uuid,'MARINES',NULL,true,now(),now()), + ('cf7f39c0-8f6b-4598-85db-f465241e66f4'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'6638a77d-4b91-48f6-8241-c87fbdddacd1'::uuid,'MARINES',NULL,true,now(),now()), + ('296a8951-19b1-4868-9937-aef61bb73106'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'97c16bc3-e174-410b-9a09-a0db31420dbc'::uuid,'MARINES',NULL,true,now(),now()), + ('cdcec10e-5300-443f-9aae-7d8ce07142b0'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'3c6c5e35-1281-45fc-83ee-e6d656e155b6'::uuid,'MARINES',NULL,true,now(),now()), + ('e638701f-f8fc-48c0-b2e0-db134b9ece1f'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'6af248be-e5a8-49e9-a9e2-516748279ab5'::uuid,'MARINES',NULL,true,now(),now()), + ('ed4f4905-b1cf-4e57-beac-bc0a2d167c71'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'3580abe3-84da-4b46-af7b-d4379e6cff46'::uuid,'MARINES',NULL,true,now(),now()), + ('2a8b16c9-99f8-43ca-a759-c4884f8f7b24'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'f3bb397e-04e6-4b37-9bf3-b3ebab79a9b6'::uuid,'MARINES',NULL,true,now(),now()), + ('70530105-a4ab-4af3-ba02-9e4cf81237fa'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'f9bfe297-4ee0-4f76-a4bd-64b3a514af5d'::uuid,'MARINES',NULL,true,now(),now()), + ('61e17c04-ed7b-4da9-816a-1b6343086d94'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'70f11a71-667b-422d-ae37-8f25539f7782'::uuid,'MARINES',NULL,true,now(),now()), + ('19d5158f-96a6-48ef-8dd9-b831c582c9c4'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'baf66a9a-3c8a-49e7-83ea-841b9960e184'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('91043072-657f-4b2a-b5d1-42d8f6a7ba38'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'e96d46b1-3ab7-4b29-b798-ec3b728dd6a1'::uuid,'MARINES',NULL,true,now(),now()), + ('a3dc835e-3989-4476-b560-006745a884bc'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'e4171b5b-d26c-43b3-b41c-68b43bcbb079'::uuid,'MARINES',NULL,true,now(),now()), + ('0cf1dc14-0c9a-4262-9cab-db73c64f6e36'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'a0651bec-1258-4e36-9c76-155247e42c0a'::uuid,'MARINES',NULL,true,now(),now()), + ('e03b53cb-386f-4e1e-ac93-cd1a9260c6b4'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'737a8e63-af19-4902-a4b5-8f80e2268e4b'::uuid,'MARINES',NULL,true,now(),now()), + ('8b3f1142-f6d4-4c2d-9e75-9ab3568742f7'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'870df26e-2c50-4512-aa2e-61094cfbc3e1'::uuid,'MARINES',NULL,true,now(),now()), + ('5f5383b8-f29e-4b9f-8798-99575440c888'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'09dc6547-d346-40c3-93fb-fb7be1fa1b3e'::uuid,'MARINES',NULL,true,now(),now()), + ('1f7a198c-5f62-4f8c-bf91-19d7cdef9bae'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'43d7d081-bb32-4544-84f1-419fe0cb76e1'::uuid,'MARINES',NULL,true,now(),now()), + ('56c17bab-a124-4710-a047-0d67d30a9610'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'d7e57942-9c83-4138-baa0-70e8b5f08598'::uuid,'MARINES',NULL,true,now(),now()), + ('aad1440d-acaf-4ba8-9564-da97ab9ba651'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'c2f06691-5989-41a3-a848-19c9f0fec5df'::uuid,'MARINES',NULL,true,now(),now()), + ('6cc24224-2298-4023-888f-5e624e585171'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'5263a9ed-ff4d-42cc-91d5-dbdefeef54d1'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('88cd8184-7e3a-48cf-bb72-a9e1c3666cc4'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'54ab3a49-6d78-4922-bac1-94a722b9859a'::uuid,'MARINES',NULL,true,now(),now()), + ('11fdac1c-7ae9-49ea-bee4-90d4582f7c6d'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'3b6fe2e9-9116-4a70-8eef-96205205b0e3'::uuid,'MARINES',NULL,true,now(),now()), + ('83c88ffb-5182-42b3-93f4-635556d8caaf'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'ba0017c8-5d48-4efe-8802-361d2f2bc16d'::uuid,'MARINES',NULL,true,now(),now()), + ('802f7a5f-8ce3-4c40-975a-83cf0ec502fc'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'af65234a-8577-4f6d-a346-7d486e963287'::uuid,'MARINES',NULL,true,now(),now()), + ('5197eed4-f6ae-4f70-b640-b67714b73f87'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'25f62695-ba9b-40c3-a7e4-0c078731123d'::uuid,'MARINES',NULL,true,now(),now()), + ('50d675ab-4064-4edb-8f1d-5739b0318ed9'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'b0422b13-4afe-443e-901f-fe652cde23a4'::uuid,'MARINES',NULL,true,now(),now()), + ('a3733567-2b57-4f12-9390-967e04bc1453'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'ecbc1d89-9cf6-4f52-b453-3ba473a0ff4e'::uuid,'MARINES',NULL,true,now(),now()), + ('6a321494-cdd8-4372-90a1-6a6c67f4e220'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'4e85cb86-e9dd-4c7c-9677-e0a327ac895c'::uuid,'MARINES',NULL,true,now(),now()), + ('99d37d3c-be31-4d3e-9c5e-c9d816a46014'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'7f9dd10d-100e-4252-9786-706349f456ca'::uuid,'MARINES',NULL,true,now(),now()), + ('e9b4c884-ad7f-4c1a-8815-05353834f5c3'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'5451f8d7-60c5-4e22-bbf6-d9af8e6ace54'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('cfaa046e-6be5-4178-a451-d368317ecb86'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'9e9cba85-7c39-4836-809d-70b54baf392e'::uuid,'MARINES',NULL,true,now(),now()), + ('9b509efb-12d9-42aa-a85a-ffb026866b56'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'f7470914-cf48-43be-b431-a3ca2fe5b290'::uuid,'MARINES',NULL,true,now(),now()), + ('7e54d995-b7bd-457c-bd97-0fd76891402e'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'5a0d3cc1-b866-4bde-b67f-78d565facf3e'::uuid,'MARINES',NULL,true,now(),now()), + ('1357aadd-b420-42c4-8a39-bab8027fa910'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'e629b95a-ec5b-4fc4-897f-e0d1050e1ec6'::uuid,'MARINES',NULL,true,now(),now()), + ('56fbbf0f-e819-41dd-802d-4e677aecd1c9'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'d68a626f-935a-4eb1-ba9b-6829feeff91c'::uuid,'MARINES',NULL,true,now(),now()), + ('bfc2dc53-896f-4f29-92c7-9a7a392b22f2'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'ed496f6e-fd34-48d1-8586-63a1d305c49c'::uuid,'MARINES',NULL,true,now(),now()), + ('d015e8b1-86ea-489f-979d-458ae35ae8d6'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'153b62b2-b1b8-4b9d-afa5-53df4150aba4'::uuid,'MARINES',NULL,true,now(),now()), + ('1f32e712-8b5e-4ae4-b409-c5c92337aed8'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'0020441b-bc0c-436e-be05-b997ca6a853c'::uuid,'MARINES',NULL,true,now(),now()), + ('a8a85e00-e657-41a2-8f32-84bdd9c92ec8'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'a8ae9bb9-e9ac-49b4-81dc-336b8a0dcb54'::uuid,'MARINES',NULL,true,now(),now()), + ('4ad1a57f-0e9e-4405-9a08-0ffa211fc8ce'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'1aa43046-8d6b-4249-88dc-b259d86c0cb8'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('dd765820-ffa5-4673-a347-fbe3464cd2d8'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'84b50723-95fc-41d1-8115-a734c7e53f66'::uuid,'MARINES',NULL,true,now(),now()), + ('6eff7586-59bd-4638-809b-5cc346646dc9'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'63dc3f78-235e-4b1c-b1db-459d7f5ae25f'::uuid,'MARINES',NULL,true,now(),now()), + ('428b0d7a-3848-4882-a5cc-80d5ae3500d6'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'4bd4c579-c163-4b1b-925a-d852d5a12642'::uuid,'MARINES',NULL,true,now(),now()), + ('9f8311f3-e191-4383-8fb8-2b58cd545dd4'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'ce7f1fc9-5b94-43cb-b398-b31cb6350d6a'::uuid,'MARINES',NULL,true,now(),now()), + ('81e3e6fe-7db3-49ff-b18c-8b078e3d129e'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'4a6a3260-e1ec-4d78-8c07-d89f1405ca16'::uuid,'MARINES',NULL,true,now(),now()), + ('4cf1c40f-60f0-4a47-a351-471720ba0fd3'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'658274b5-720c-4a70-ba9d-249614d85ebc'::uuid,'MARINES',NULL,true,now(),now()), + ('0754978b-d11e-4f2c-b59c-3252d2735b26'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'41f0736c-6d26-4e93-9668-860e9f0e222a'::uuid,'MARINES',NULL,true,now(),now()), + ('c9b8305d-2e16-46a7-9b7c-b3edeb6f8e93'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'c2a8e8c3-dddc-4c0f-a23a-0b4e2c20af0d'::uuid,'MARINES',NULL,true,now(),now()), + ('a8768e6d-1a6d-449a-9f2e-2e198dcd6e00'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'0f1e1c87-0497-4ee2-970d-21ac2d2155db'::uuid,'MARINES',NULL,true,now(),now()), + ('cf292129-d543-4632-9cb3-b074279e42be'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'e40b411d-55f0-4470-83d0-0bbe11fa77dd'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('3f07cedf-ad90-465e-95a5-ce44a2f088b8'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'2632b4e5-c6cb-4e64-8924-0b7e4b1115ec'::uuid,'MARINES',NULL,true,now(),now()), + ('42a2d93b-9dea-4c63-b0a7-c39364aacf75'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'1336deb9-5c87-409d-8051-4ab9f211eb29'::uuid,'MARINES',NULL,true,now(),now()), + ('64fe0d14-98f7-4f73-9aa3-a19617b2d8c3'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'7a6d3b5b-81a6-4db5-b2ab-ecbfd6bd7941'::uuid,'MARINES',NULL,true,now(),now()), + ('3ab04740-9f13-47f8-a80e-b63ab5b67590'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'91b69254-5976-4839-a31d-972e9958d9cf'::uuid,'MARINES',NULL,true,now(),now()), + ('3be8e483-6bce-4a7d-a3bd-fc1485e79818'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'166f3629-79b9-451a-90a3-c43680929a2f'::uuid,'MARINES',NULL,true,now(),now()), + ('ddf69dcb-345e-47c1-a585-ce24d0854de5'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'4fe05eb4-1b1c-4d4a-a185-0b039ac64835'::uuid,'MARINES',NULL,true,now(),now()), + ('fe9e365f-98a5-4658-a58d-5f8279ff3e5a'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'ca8aecda-4642-45c7-96ed-309c35c4b78f'::uuid,'MARINES',NULL,true,now(),now()), + ('2051a441-f4d0-4b6e-8614-74761de505e6'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'e4bc9404-5466-4a41-993e-09474266afc3'::uuid,'MARINES',NULL,true,now(),now()), + ('6797daed-f002-431c-829b-dab7c1b16ff2'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'d4a51d90-3945-4ad3-9cba-a18d8d7b34d7'::uuid,'MARINES',NULL,true,now(),now()), + ('2398bf11-1986-4914-8e47-6afac423283a'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'a5a60d63-d9a8-4bde-9081-f011784b2d31'::uuid,'MARINES',NULL,true,now(),now()); +INSERT INTO gbloc_aors (id,jppso_regions_id,oconus_rate_area_id,department_indicator,shipment_type,is_active,created_at,updated_at) VALUES + ('f3a6c247-4c4c-4f45-9162-50307d4711f5'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'93842d74-1f3e-46cd-aca9-9f0dafbd20a1'::uuid,'MARINES',NULL,true,now(),now()), + ('0a82b196-7c24-4214-9155-05f0c5c2d7e9'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'1bc0dbda-f0ce-4b76-a551-78dbaaa9e3ec'::uuid,'MARINES',NULL,true,now(),now()), + ('af0a2db3-2e2e-4a78-804b-9a8b89b96e12'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'f1a7ef90-cfa6-4e0c-92c3-f8d70c07ba4d'::uuid,'MARINES',NULL,true,now(),now()), + ('5205a965-d424-469a-9526-17ef551685e6'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'9c5b4c4d-e05c-42ca-bd77-b61f5d8c7afc'::uuid,'MARINES',NULL,true,now(),now()), + ('f263fdb0-933b-42a7-925e-a9852c5804fa'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'27ec2576-78dd-4605-b1a8-0b9ca207fc26'::uuid,'MARINES',NULL,true,now(),now()), + ('b03c7ae3-4d6d-445c-b94a-73af723e5226'::uuid,'85b324ae-1a0d-4f7d-971f-ea509dcc73d7'::uuid,'12396ebc-59e9-430a-8475-759a38af6b7a'::uuid,'MARINES',NULL,true,now(),now()); + + +drop view move_to_gbloc; +CREATE OR REPLACE VIEW move_to_gbloc AS +SELECT move_id, gbloc FROM ( + SELECT DISTINCT ON (sh.move_id) sh.move_id, s.affiliation, + COALESCE(pctg.gbloc, coalesce(pctg_oconus_bos.gbloc, coalesce(pctg_oconus.gbloc, pctg_ppm.gbloc))) AS gbloc + FROM mto_shipments sh + JOIN moves m ON sh.move_id = m.id + JOIN orders o on m.orders_id = o.id + JOIN service_members s on o.service_member_id = s.id + LEFT JOIN ( SELECT a.id AS address_id, + pctg_1.gbloc, pctg_1.postal_code + FROM addresses a + JOIN postal_code_to_gblocs pctg_1 ON a.postal_code::text = pctg_1.postal_code::text) pctg ON pctg.address_id = sh.pickup_address_id + LEFT JOIN ( SELECT ppm.shipment_id, + pctg_1.gbloc + FROM ppm_shipments ppm + JOIN addresses ppm_address ON ppm.pickup_postal_address_id = ppm_address.id + JOIN postal_code_to_gblocs pctg_1 ON ppm_address.postal_code::text = pctg_1.postal_code::text) pctg_ppm ON pctg_ppm.shipment_id = sh.id + LEFT JOIN ( SELECT a.id AS address_id, + cast(jr.code as varchar) AS gbloc, ga.department_indicator + FROM addresses a + JOIN re_oconus_rate_areas ora ON a.us_post_region_cities_id = ora.us_post_region_cities_id + JOIN gbloc_aors ga ON ora.id = ga.oconus_rate_area_id + JOIN jppso_regions jr ON ga.jppso_regions_id = jr.id + ) pctg_oconus_bos ON pctg_oconus_bos.address_id = sh.pickup_address_id + and case when s.affiliation = 'AIR_FORCE' THEN 'AIR_AND_SPACE_FORCE' + when s.affiliation = 'SPACE_FORCE' THEN 'AIR_AND_SPACE_FORCE' + else s.affiliation + end = pctg_oconus_bos.department_indicator + LEFT JOIN ( SELECT a.id AS address_id, + cast(pctg_1.code as varchar) AS gbloc, ga.department_indicator + FROM addresses a + JOIN re_oconus_rate_areas ora ON a.us_post_region_cities_id = ora.us_post_region_cities_id + JOIN gbloc_aors ga ON ora.id = ga.oconus_rate_area_id + JOIN jppso_regions pctg_1 ON ga.jppso_regions_id = pctg_1.id + ) pctg_oconus ON pctg_oconus.address_id = sh.pickup_address_id and pctg_oconus.department_indicator is null + WHERE sh.deleted_at IS NULL + ORDER BY sh.move_id, sh.created_at) as m; + + CREATE OR REPLACE VIEW move_to_dest_gbloc AS SELECT distinct move_id, gbloc FROM ( @@ -25,8 +232,6 @@ SELECT distinct move_id, gbloc FROM ( ) pctg_oconus_bos ON pctg_oconus_bos.address_id = sh.destination_address_id and case when s.affiliation = 'AIR_FORCE' THEN 'AIR_AND_SPACE_FORCE' when s.affiliation = 'SPACE_FORCE' THEN 'AIR_AND_SPACE_FORCE' - when s.affiliation = 'NAVY' THEN 'NAVY_AND_MARINES' - when s.affiliation = 'MARINES' THEN 'NAVY_AND_MARINES' else s.affiliation end = pctg_oconus_bos.department_indicator LEFT JOIN ( SELECT a.id AS address_id, @@ -112,18 +317,24 @@ BEGIN ) )::JSONB AS orders, COALESCE( - json_agg( - json_build_object( - ''id'', mto_shipments.id, - ''shipment_type'', mto_shipments.shipment_type, - ''status'', mto_shipments.status, - ''requested_pickup_date'', TO_CHAR(mto_shipments.requested_pickup_date, ''YYYY-MM-DD"T00:00:00Z"''), - ''scheduled_pickup_date'', TO_CHAR(mto_shipments.scheduled_pickup_date, ''YYYY-MM-DD"T00:00:00Z"''), - ''requested_delivery_date'', TO_CHAR(mto_shipments.requested_delivery_date, ''YYYY-MM-DD"T00:00:00Z"''), - ''approved_date'', TO_CHAR(mto_shipments.approved_date, ''YYYY-MM-DD"T00:00:00Z"''), - ''prime_estimated_weight'', mto_shipments.prime_estimated_weight + ( + SELECT json_agg( + json_build_object( + ''id'', ms.id, + ''shipment_type'', ms.shipment_type, + ''status'', ms.status, + ''requested_pickup_date'', TO_CHAR(ms.requested_pickup_date, ''YYYY-MM-DD"T00:00:00Z"''), + ''scheduled_pickup_date'', TO_CHAR(ms.scheduled_pickup_date, ''YYYY-MM-DD"T00:00:00Z"''), + ''approved_date'', TO_CHAR(ms.approved_date, ''YYYY-MM-DD"T00:00:00Z"''), + ''prime_estimated_weight'', ms.prime_estimated_weight + ) ) - ) FILTER (WHERE mto_shipments.id IS NOT NULL), + FROM ( + SELECT DISTINCT ON (mto_shipments.id) mto_shipments.* + FROM mto_shipments + WHERE mto_shipments.move_id = moves.id + ) AS ms + ), ''[]'' )::JSONB AS mto_shipments, json_build_object( @@ -152,19 +363,8 @@ BEGIN WHERE moves.show = TRUE '; - -- add destination queue-specific filters (pending dest address requests, dest SIT & shuttle service items) - sql_query := sql_query || ' - AND ( - shipment_address_updates.status = ''REQUESTED'' - OR ( - mto_service_items.status = ''SUBMITTED'' - AND re_services.code IN (''DDFSIT'', ''DDASIT'', ''DDDSIT'', ''DDSHUT'', ''DDSFSC'', ''IDFSIT'', ''IDASIT'', ''IDDSIT'', ''IDSHUT'') - ) - ) - '; - IF user_gbloc IS NOT NULL THEN - sql_query := sql_query || ' AND move_to_gbloc.gbloc = $1 '; + sql_query := sql_query || ' AND move_to_dest_gbloc.gbloc = $1 '; END IF; IF customer_name IS NOT NULL THEN @@ -218,6 +418,17 @@ BEGIN sql_query := sql_query || ' AND (too_user.first_name || '' '' || too_user.last_name) ILIKE ''%'' || $12 || ''%'' '; END IF; + -- add destination queue-specific filters (pending dest address requests, dest SIT & shuttle service items) + sql_query := sql_query || ' + AND ( + shipment_address_updates.status = ''REQUESTED'' + OR ( + mto_service_items.status = ''SUBMITTED'' + AND re_services.code IN (''DDFSIT'', ''DDASIT'', ''DDDSIT'', ''DDSHUT'', ''DDSFSC'', ''IDFSIT'', ''IDASIT'', ''IDDSIT'', ''IDSHUT'') + ) + ) + '; + sql_query := sql_query || ' GROUP BY moves.id, diff --git a/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx b/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx index 44b11a5567d..11094276bba 100644 --- a/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx +++ b/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx @@ -609,7 +609,7 @@ const ServicesCounselingQueue = ({ return ; }; - if (queueType === 'Search') { + if (queueType === 'search') { return (
{renderNavBar()} From 711252076cc756a240ccfed7bf32ebd422787547 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Tue, 4 Feb 2025 22:19:22 +0000 Subject: [PATCH 07/15] dear gawd I think I have it working - need tests --- ...nation_queue_db_func_and_gbloc_view.up.sql | 48 +++++++++++++++++-- pkg/services/order/order_fetcher.go | 8 ++-- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql b/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql index bd09ad533c4..8aabdaeddf0 100644 --- a/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql +++ b/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql @@ -205,11 +205,12 @@ SELECT move_id, gbloc FROM ( ORDER BY sh.move_id, sh.created_at) as m; +-- used for the destination queue CREATE OR REPLACE VIEW move_to_dest_gbloc AS SELECT distinct move_id, gbloc FROM ( SELECT sh.move_id, s.affiliation, - COALESCE(pctg.gbloc, coalesce(pctg_oconus_bos.gbloc, coalesce(pctg_oconus.gbloc, pctg_ppm.gbloc))) AS gbloc + COALESCE(case when s.affiliation = 'MARINES' then 'USMC' else pctg.gbloc end, coalesce(pctg_oconus_bos.gbloc, coalesce(pctg_oconus.gbloc, pctg_ppm.gbloc))) AS gbloc FROM mto_shipments sh JOIN moves m ON sh.move_id = m.id JOIN orders o on m.orders_id = o.id @@ -244,7 +245,7 @@ SELECT distinct move_id, gbloc FROM ( WHERE sh.deleted_at IS NULL) as m; -- database function that returns a list of moves that have destination requests --- this includes shipment address update requests & destination service items +-- this includes shipment address update requests, destination SIT, & destination shuttle CREATE OR REPLACE FUNCTION get_destination_queue( user_gbloc TEXT DEFAULT NULL, customer_name TEXT DEFAULT NULL, @@ -259,7 +260,9 @@ CREATE OR REPLACE FUNCTION get_destination_queue( counseling_office TEXT DEFAULT NULL, too_assigned_user TEXT DEFAULT NULL, page INTEGER DEFAULT 1, - per_page INTEGER DEFAULT 20 + per_page INTEGER DEFAULT 20, + sort TEXT DEFAULT NULL, + sort_direction TEXT DEFAULT NULL ) RETURNS TABLE ( id UUID, @@ -279,6 +282,8 @@ RETURNS TABLE ( DECLARE sql_query TEXT; offset_value INTEGER; + sort_column TEXT; + sort_order TEXT; BEGIN IF page < 1 THEN page := 1; @@ -418,7 +423,7 @@ BEGIN sql_query := sql_query || ' AND (too_user.first_name || '' '' || too_user.last_name) ILIKE ''%'' || $12 || ''%'' '; END IF; - -- add destination queue-specific filters (pending dest address requests, dest SIT & shuttle service items) + -- add destination queue-specific filters (pending dest address requests, dest SIT & dest shuttle service items) sql_query := sql_query || ' AND ( shipment_address_updates.status = ''REQUESTED'' @@ -429,6 +434,36 @@ BEGIN ) '; + -- default sorting values if none are provided (move.id) + sort_column := 'id'; + sort_order := 'ASC'; + + IF sort IS NOT NULL THEN + CASE sort + WHEN 'locator' THEN sort_column := 'moves.locator'; + WHEN 'status' THEN sort_column := 'moves.status'; + WHEN 'customerName' THEN sort_column := 'service_members.last_name'; + WHEN 'edipi' THEN sort_column := 'service_members.edipi'; + WHEN 'emplid' THEN sort_column := 'service_members.emplid'; + WHEN 'requestedMoveDate' THEN sort_column := 'COALESCE(mto_shipments.requested_pickup_date, ppm_shipments.expected_departure_date, mto_shipments.requested_delivery_date)'; + WHEN 'appearedInTooAt' THEN sort_column := 'COALESCE(moves.submitted_at, moves.approvals_requested_at)'; + WHEN 'branch' THEN sort_column := 'service_members.affiliation'; + WHEN 'originDutyLocation' THEN sort_column := 'origin_duty_locations.name'; + WHEN 'counselingOffice' THEN sort_column := 'counseling_offices.name'; + WHEN 'assignedTo' THEN sort_column := 'too_user.last_name'; + ELSE + sort_column := 'moves.id'; + END CASE; + END IF; + + IF sort_direction IS NOT NULL THEN + IF LOWER(sort_direction) = 'desc' THEN + sort_order := 'DESC'; + ELSE + sort_order := 'ASC'; + END IF; + END IF; + sql_query := sql_query || ' GROUP BY moves.id, @@ -439,6 +474,9 @@ BEGIN moves.locked_by, moves.too_assigned_id, moves.counseling_transportation_office_id, + mto_shipments.requested_pickup_date, + mto_shipments.requested_delivery_date, + ppm_shipments.expected_departure_date, orders.id, service_members.id, service_members.first_name, @@ -450,7 +488,7 @@ BEGIN counseling_offices.name, too_user.first_name, too_user.last_name'; - sql_query := sql_query || ' ORDER BY moves.id ASC '; + sql_query := sql_query || format(' ORDER BY %s %s ', sort_column, sort_order); sql_query := sql_query || ' LIMIT $13 OFFSET $14 '; RETURN QUERY EXECUTE sql_query diff --git a/pkg/services/order/order_fetcher.go b/pkg/services/order/order_fetcher.go index 53478c9860d..b0e59912f39 100644 --- a/pkg/services/order/order_fetcher.go +++ b/pkg/services/order/order_fetcher.go @@ -348,7 +348,7 @@ func (f orderFetcher) ListDestinationRequestsOrders(appCtx appcontext.AppContext } // calling the database function with all passed in parameters - err := appCtx.DB().RawQuery("SELECT * FROM get_destination_queue($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + err := appCtx.DB().RawQuery("SELECT * FROM get_destination_queue($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)", officeUserGbloc, params.CustomerName, params.Edipi, @@ -356,13 +356,15 @@ func (f orderFetcher) ListDestinationRequestsOrders(appCtx appcontext.AppContext pq.Array(params.Status), params.Locator, params.RequestedMoveDate, - params.SubmittedAt, + params.AppearedInTOOAt, params.Branch, strings.Join(params.OriginDutyLocation, " "), params.CounselingOffice, params.TOOAssignedUser, params.Page, - params.PerPage). + params.PerPage, + params.Sort, + params.Order). All(&movesWithCount) if err != nil { From df78bf31aa5dc7482904cc236b8e07dad2948729 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Tue, 4 Feb 2025 22:48:25 +0000 Subject: [PATCH 08/15] cleanup before adding tests --- pkg/gen/ghcapi/embedded_spec.go | 32 ++--------- .../queues/get_destination_requests_queue.go | 4 +- ...t_destination_requests_queue_parameters.go | 55 ------------------- ...t_destination_requests_queue_urlbuilder.go | 18 ------ pkg/handlers/ghcapi/queues.go | 18 ++---- pkg/services/order/order_fetcher.go | 22 +++----- src/pages/Office/MoveQueue/MoveQueue.test.jsx | 3 +- .../ServicesCounselingQueue.jsx | 2 +- swagger-def/ghc.yaml | 13 +---- swagger/ghc.yaml | 22 ++------ 10 files changed, 32 insertions(+), 157 deletions(-) diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go index 221fa8971bd..0704324506f 100644 --- a/pkg/gen/ghcapi/embedded_spec.go +++ b/pkg/gen/ghcapi/embedded_spec.go @@ -4678,14 +4678,14 @@ func init() { }, "/queues/destination-requests": { "get": { - "description": "A TOO will view this queue when they have destination requests tied to their GBLOC. This includes unapproved destination SIT service items (including shuttle) and destination address requests that are not approved.\n", + "description": "A TOO will view this queue when they have destination requests tied to their GBLOC. This includes unapproved destination SIT service items, destination shuttle service items and destination address requests that are not yet approved by the TOO.\n", "produces": [ "application/json" ], "tags": [ "queues" ], - "summary": "Gets queued list of all customer moves by GBLOC that have destination requests (destination SIT, shuttle, address requests)", + "summary": "Gets queued list of all customer moves by GBLOC that have both CONUS \u0026 OCONUS destination requests (destination SIT, destination shuttle, address requests)", "operationId": "getDestinationRequestsQueue", "parameters": [ { @@ -4797,18 +4797,6 @@ func init() { "name": "status", "in": "query" }, - { - "type": "string", - "description": "order type", - "name": "orderType", - "in": "query" - }, - { - "type": "string", - "description": "Used to return a queue for a GBLOC other than the default of the current user. Requires the HQ role. The parameter is ignored if the requesting user does not have the necessary role.\n", - "name": "viewAsGBLOC", - "in": "query" - }, { "type": "string", "description": "Used to illustrate which user is assigned to this move.\n", @@ -21506,14 +21494,14 @@ func init() { }, "/queues/destination-requests": { "get": { - "description": "A TOO will view this queue when they have destination requests tied to their GBLOC. This includes unapproved destination SIT service items (including shuttle) and destination address requests that are not approved.\n", + "description": "A TOO will view this queue when they have destination requests tied to their GBLOC. This includes unapproved destination SIT service items, destination shuttle service items and destination address requests that are not yet approved by the TOO.\n", "produces": [ "application/json" ], "tags": [ "queues" ], - "summary": "Gets queued list of all customer moves by GBLOC that have destination requests (destination SIT, shuttle, address requests)", + "summary": "Gets queued list of all customer moves by GBLOC that have both CONUS \u0026 OCONUS destination requests (destination SIT, destination shuttle, address requests)", "operationId": "getDestinationRequestsQueue", "parameters": [ { @@ -21625,18 +21613,6 @@ func init() { "name": "status", "in": "query" }, - { - "type": "string", - "description": "order type", - "name": "orderType", - "in": "query" - }, - { - "type": "string", - "description": "Used to return a queue for a GBLOC other than the default of the current user. Requires the HQ role. The parameter is ignored if the requesting user does not have the necessary role.\n", - "name": "viewAsGBLOC", - "in": "query" - }, { "type": "string", "description": "Used to illustrate which user is assigned to this move.\n", diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue.go b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue.go index f4fdf4135d3..0bc440cf200 100644 --- a/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue.go +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue.go @@ -32,9 +32,9 @@ func NewGetDestinationRequestsQueue(ctx *middleware.Context, handler GetDestinat /* GetDestinationRequestsQueue swagger:route GET /queues/destination-requests queues getDestinationRequestsQueue -Gets queued list of all customer moves by GBLOC that have destination requests (destination SIT, shuttle, address requests) +Gets queued list of all customer moves by GBLOC that have both CONUS & OCONUS destination requests (destination SIT, destination shuttle, address requests) -A TOO will view this queue when they have destination requests tied to their GBLOC. This includes unapproved destination SIT service items (including shuttle) and destination address requests that are not approved. +A TOO will view this queue when they have destination requests tied to their GBLOC. This includes unapproved destination SIT service items, destination shuttle service items and destination address requests that are not yet approved by the TOO. */ type GetDestinationRequestsQueue struct { Context *middleware.Context diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_parameters.go b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_parameters.go index 86930ef5fab..dac60111a5d 100644 --- a/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_parameters.go +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_parameters.go @@ -75,10 +75,6 @@ type GetDestinationRequestsQueueParams struct { In: query */ Order *string - /*order type - In: query - */ - OrderType *string /* Unique: true In: query @@ -106,11 +102,6 @@ type GetDestinationRequestsQueueParams struct { In: query */ Status []string - /*Used to return a queue for a GBLOC other than the default of the current user. Requires the HQ role. The parameter is ignored if the requesting user does not have the necessary role. - - In: query - */ - ViewAsGBLOC *string } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface @@ -174,11 +165,6 @@ func (o *GetDestinationRequestsQueueParams) BindRequest(r *http.Request, route * res = append(res, err) } - qOrderType, qhkOrderType, _ := qs.GetOK("orderType") - if err := o.bindOrderType(qOrderType, qhkOrderType, route.Formats); err != nil { - res = append(res, err) - } - qOriginDutyLocation, qhkOriginDutyLocation, _ := qs.GetOK("originDutyLocation") if err := o.bindOriginDutyLocation(qOriginDutyLocation, qhkOriginDutyLocation, route.Formats); err != nil { res = append(res, err) @@ -208,11 +194,6 @@ func (o *GetDestinationRequestsQueueParams) BindRequest(r *http.Request, route * if err := o.bindStatus(qStatus, qhkStatus, route.Formats); err != nil { res = append(res, err) } - - qViewAsGBLOC, qhkViewAsGBLOC, _ := qs.GetOK("viewAsGBLOC") - if err := o.bindViewAsGBLOC(qViewAsGBLOC, qhkViewAsGBLOC, route.Formats); err != nil { - res = append(res, err) - } if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -432,24 +413,6 @@ func (o *GetDestinationRequestsQueueParams) validateOrder(formats strfmt.Registr return nil } -// bindOrderType binds and validates parameter OrderType from query. -func (o *GetDestinationRequestsQueueParams) bindOrderType(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.OrderType = &raw - - return nil -} - // bindOriginDutyLocation binds and validates array parameter OriginDutyLocation from query. // // Arrays are parsed according to CollectionFormat: "multi" (defaults to "csv" when empty). @@ -624,21 +587,3 @@ func (o *GetDestinationRequestsQueueParams) validateStatus(formats strfmt.Regist } return nil } - -// bindViewAsGBLOC binds and validates parameter ViewAsGBLOC from query. -func (o *GetDestinationRequestsQueueParams) bindViewAsGBLOC(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.ViewAsGBLOC = &raw - - return nil -} diff --git a/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_urlbuilder.go b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_urlbuilder.go index c267cfe2aa8..c8b86e869dc 100644 --- a/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_urlbuilder.go +++ b/pkg/gen/ghcapi/ghcoperations/queues/get_destination_requests_queue_urlbuilder.go @@ -26,14 +26,12 @@ type GetDestinationRequestsQueueURL struct { Emplid *string Locator *string Order *string - OrderType *string OriginDutyLocation []string Page *int64 PerPage *int64 RequestedMoveDate *string Sort *string Status []string - ViewAsGBLOC *string _basePath string // avoid unkeyed usage @@ -149,14 +147,6 @@ func (o *GetDestinationRequestsQueueURL) Build() (*url.URL, error) { qs.Set("order", orderQ) } - var orderTypeQ string - if o.OrderType != nil { - orderTypeQ = *o.OrderType - } - if orderTypeQ != "" { - qs.Set("orderType", orderTypeQ) - } - var originDutyLocationIR []string for _, originDutyLocationI := range o.OriginDutyLocation { originDutyLocationIS := originDutyLocationI @@ -220,14 +210,6 @@ func (o *GetDestinationRequestsQueueURL) Build() (*url.URL, error) { } } - var viewAsGBLOCQ string - if o.ViewAsGBLOC != nil { - viewAsGBLOCQ = *o.ViewAsGBLOC - } - if viewAsGBLOCQ != "" { - qs.Set("viewAsGBLOC", viewAsGBLOCQ) - } - _result.RawQuery = qs.Encode() return &_result, nil diff --git a/pkg/handlers/ghcapi/queues.go b/pkg/handlers/ghcapi/queues.go index 8e779d1123f..f4b124cbb1c 100644 --- a/pkg/handlers/ghcapi/queues.go +++ b/pkg/handlers/ghcapi/queues.go @@ -191,9 +191,9 @@ func (h GetDestinationRequestsQueueHandler) Handle(params queues.GetDestinationR return h.AuditableAppContextFromRequestWithErrors(params.HTTPRequest, func(appCtx appcontext.AppContext) (middleware.Responder, error) { if !appCtx.Session().IsOfficeUser() || - (!appCtx.Session().Roles.HasRole(roles.RoleTypeTOO) && !appCtx.Session().Roles.HasRole(roles.RoleTypeHQ)) { + (!appCtx.Session().Roles.HasRole(roles.RoleTypeTOO)) { forbiddenErr := apperror.NewForbiddenError( - "user is not authenticated with TOO or HQ office role", + "user is not authenticated with TOO role", ) appCtx.Logger().Error(forbiddenErr.Error()) return queues.NewGetDestinationRequestsQueueForbidden(), forbiddenErr @@ -214,28 +214,23 @@ func (h GetDestinationRequestsQueueHandler) Handle(params queues.GetDestinationR PerPage: params.PerPage, Sort: params.Sort, Order: params.Order, - OrderType: params.OrderType, TOOAssignedUser: params.AssignedTo, CounselingOffice: params.CounselingOffice, } + // we only care about moves in APPROVALS REQUESTED status if params.Status == nil { ListOrderParams.Status = []string{string(models.MoveStatusAPPROVALSREQUESTED)} } - // 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 for perPage. + // default pagination values if params.Page == nil { ListOrderParams.Page = models.Int64Pointer(1) } - // Same for perPage if params.PerPage == nil { ListOrderParams.PerPage = models.Int64Pointer(20) } - if params.ViewAsGBLOC != nil && appCtx.Session().Roles.HasRole(roles.RoleTypeHQ) { - ListOrderParams.ViewAsGBLOC = params.ViewAsGBLOC - } - moves, count, err := h.OrderFetcher.ListDestinationRequestsOrders( appCtx, appCtx.Session().OfficeUserID, @@ -244,7 +239,7 @@ func (h GetDestinationRequestsQueueHandler) Handle(params queues.GetDestinationR ) if err != nil { appCtx.Logger(). - Error("error fetching list of moves for office user", zap.Error(err)) + Error("error fetching destinaton queue for office user", zap.Error(err)) return queues.NewGetDestinationRequestsQueueInternalServerError(), err } @@ -269,7 +264,7 @@ func (h GetDestinationRequestsQueueHandler) Handle(params queues.GetDestinationR return queues.NewGetDestinationRequestsQueueInternalServerError(), err } - // if the TOO/office user is accessing the queue, we need to unlock move/moves they have locked + // if the TOO is accessing the queue, we need to unlock move/moves they have locked if appCtx.Session().IsOfficeUser() { officeUserID := appCtx.Session().OfficeUserID for i, move := range moves { @@ -283,7 +278,6 @@ func (h GetDestinationRequestsQueueHandler) Handle(params queues.GetDestinationR moves[i] = *unlockedMove } } - // checking if moves that are NOT in their queue are locked by the user (using search, etc) err := h.CheckForLockedMovesAndUnlock(appCtx, officeUserID) if err != nil { appCtx.Logger().Error(fmt.Sprintf("failed to unlock moves for office user ID: %s", officeUserID), zap.Error(err)) diff --git a/pkg/services/order/order_fetcher.go b/pkg/services/order/order_fetcher.go index b0e59912f39..500cdbad41f 100644 --- a/pkg/services/order/order_fetcher.go +++ b/pkg/services/order/order_fetcher.go @@ -310,6 +310,7 @@ func (f orderFetcher) ListOrders(appCtx appcontext.AppContext, officeUserID uuid return moves, count, nil } +// this is a custom/temporary struct used in the below service object to get destination queue moves type MoveWithCount struct { models.Move OrdersRaw json.RawMessage `json:"orders" db:"orders"` @@ -335,16 +336,10 @@ func (f orderFetcher) ListDestinationRequestsOrders(appCtx appcontext.AppContext var movesWithCount []MoveWithCount // getting the office user's GBLOC - var officeUserGbloc string - if params.ViewAsGBLOC != nil { - officeUserGbloc = *params.ViewAsGBLOC - } else { - var gblocErr error - gblocFetcher := officeuser.NewOfficeUserGblocFetcher() - officeUserGbloc, gblocErr = gblocFetcher.FetchGblocForOfficeUser(appCtx, officeUserID) - if gblocErr != nil { - return []models.Move{}, 0, gblocErr - } + gblocFetcher := officeuser.NewOfficeUserGblocFetcher() + officeUserGbloc, gblocErr := gblocFetcher.FetchGblocForOfficeUser(appCtx, officeUserID) + if gblocErr != nil { + return []models.Move{}, 0, gblocErr } // calling the database function with all passed in parameters @@ -371,7 +366,7 @@ func (f orderFetcher) ListDestinationRequestsOrders(appCtx appcontext.AppContext return []models.Move{}, 0, err } - // each row is sent back with the total count, so we will take the value from the first one + // each row is sent back with the total count from the db func, so we will take the value from the first one var count int64 if len(movesWithCount) > 0 { count = movesWithCount[0].TotalCount @@ -414,11 +409,12 @@ func (f orderFetcher) ListDestinationRequestsOrders(appCtx appcontext.AppContext movesWithCount[i].TOOAssignedUser = &tooAssigned } - // the handler consumes a Move object, so we have to copy our custom struct into the Move struct + // the handler consumes a Move object and NOT the MoveWithCount struct used in this func + // so we have to copy our custom struct into the Move struct for _, moveWithCount := range movesWithCount { var move models.Move if err := copier.Copy(&move, &moveWithCount); err != nil { - return nil, 0, fmt.Errorf("error copying movesWithCount into Moves: %w", err) + return nil, 0, fmt.Errorf("error copying movesWithCount into Moves struct: %w", err) } moves = append(moves, move) } diff --git a/src/pages/Office/MoveQueue/MoveQueue.test.jsx b/src/pages/Office/MoveQueue/MoveQueue.test.jsx index 4e5ec01cd33..87defd030a8 100644 --- a/src/pages/Office/MoveQueue/MoveQueue.test.jsx +++ b/src/pages/Office/MoveQueue/MoveQueue.test.jsx @@ -328,7 +328,7 @@ describe('MoveQueue', () => { wrapper.update(); expect(wrapper.find('[data-testid="multi-value-container"]').text()).toEqual('New move'); }); - it('renders Search and Move Queue tabs', () => { + it('renders Search, Destination Queue and Move Queue tabs', () => { reactRouterDom.useParams.mockReturnValue({ queueType: generalRoutes.QUEUE_SEARCH_PATH }); render( @@ -338,6 +338,7 @@ describe('MoveQueue', () => { expect(screen.getByTestId('closeout-tab-link')).toBeInTheDocument(); expect(screen.getByTestId('search-tab-link')).toBeInTheDocument(); expect(screen.getByText('Task Order Queue', { selector: 'span' })).toBeInTheDocument(); + expect(screen.getByText('Destination Requests Queue', { selector: 'span' })).toBeInTheDocument(); expect(screen.getByText('Search', { selector: 'span' })).toBeInTheDocument(); }); it('renders TableQueue when Search tab is selected', () => { diff --git a/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx b/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx index 11094276bba..6518eda8080 100644 --- a/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx +++ b/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx @@ -609,7 +609,7 @@ const ServicesCounselingQueue = ({ return ; }; - if (queueType === 'search') { + if (queueType === generalRoutes.QUEUE_SEARCH_PATH) { return (
{renderNavBar()} diff --git a/swagger-def/ghc.yaml b/swagger-def/ghc.yaml index 7ed8e6f8cdc..4437679cf39 100644 --- a/swagger-def/ghc.yaml +++ b/swagger-def/ghc.yaml @@ -3739,9 +3739,9 @@ paths: get: produces: - application/json - summary: Gets queued list of all customer moves by GBLOC that have destination requests (destination SIT, shuttle, address requests) + summary: Gets queued list of all customer moves by GBLOC that have both CONUS & OCONUS destination requests (destination SIT, destination shuttle, address requests) description: > - A TOO will view this queue when they have destination requests tied to their GBLOC. This includes unapproved destination SIT service items (including shuttle) and destination address requests that are not approved. + A TOO will view this queue when they have destination requests tied to their GBLOC. This includes unapproved destination SIT service items, destination shuttle service items and destination address requests that are not yet approved by the TOO. operationId: getDestinationRequestsQueue tags: - queues @@ -3822,15 +3822,6 @@ paths: - SUBMITTED - SERVICE COUNSELING COMPLETED - APPROVALS REQUESTED - - in: query - name: orderType - type: string - description: order type - - in: query - name: viewAsGBLOC - type: string - description: | - Used to return a queue for a GBLOC other than the default of the current user. Requires the HQ role. The parameter is ignored if the requesting user does not have the necessary role. - in: query name: assignedTo type: string diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml index ce3f29058b9..7b507636923 100644 --- a/swagger/ghc.yaml +++ b/swagger/ghc.yaml @@ -3906,13 +3906,14 @@ paths: produces: - application/json summary: >- - Gets queued list of all customer moves by GBLOC that have destination - requests (destination SIT, shuttle, address requests) + Gets queued list of all customer moves by GBLOC that have both CONUS & + OCONUS destination requests (destination SIT, destination shuttle, + address requests) description: > A TOO will view this queue when they have destination requests tied to - their GBLOC. This includes unapproved destination SIT service items - (including shuttle) and destination address requests that are not - approved. + their GBLOC. This includes unapproved destination SIT service items, + destination shuttle service items and destination address requests that + are not yet approved by the TOO. operationId: getDestinationRequestsQueue tags: - queues @@ -3993,17 +3994,6 @@ paths: - SUBMITTED - SERVICE COUNSELING COMPLETED - APPROVALS REQUESTED - - in: query - name: orderType - type: string - description: order type - - in: query - name: viewAsGBLOC - type: string - description: > - Used to return a queue for a GBLOC other than the default of the - current user. Requires the HQ role. The parameter is ignored if the - requesting user does not have the necessary role. - in: query name: assignedTo type: string From 0aa8ab6cebb821d4d7bab26bf6cd040b26eea32f Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Tue, 4 Feb 2025 22:52:59 +0000 Subject: [PATCH 09/15] are you mocking me? --- pkg/services/mocks/WeightAllotmentFetcher.go | 117 +++++++++++++++++++ pkg/services/mocks/WeightRestrictor.go | 89 ++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 pkg/services/mocks/WeightAllotmentFetcher.go create mode 100644 pkg/services/mocks/WeightRestrictor.go diff --git a/pkg/services/mocks/WeightAllotmentFetcher.go b/pkg/services/mocks/WeightAllotmentFetcher.go new file mode 100644 index 00000000000..fa36bfbee2e --- /dev/null +++ b/pkg/services/mocks/WeightAllotmentFetcher.go @@ -0,0 +1,117 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + appcontext "github.com/transcom/mymove/pkg/appcontext" + internalmessages "github.com/transcom/mymove/pkg/gen/internalmessages" + + mock "github.com/stretchr/testify/mock" + + models "github.com/transcom/mymove/pkg/models" +) + +// WeightAllotmentFetcher is an autogenerated mock type for the WeightAllotmentFetcher type +type WeightAllotmentFetcher struct { + mock.Mock +} + +// GetAllWeightAllotments provides a mock function with given fields: appCtx +func (_m *WeightAllotmentFetcher) GetAllWeightAllotments(appCtx appcontext.AppContext) (map[internalmessages.OrderPayGrade]models.WeightAllotment, error) { + ret := _m.Called(appCtx) + + if len(ret) == 0 { + panic("no return value specified for GetAllWeightAllotments") + } + + var r0 map[internalmessages.OrderPayGrade]models.WeightAllotment + var r1 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext) (map[internalmessages.OrderPayGrade]models.WeightAllotment, error)); ok { + return rf(appCtx) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext) map[internalmessages.OrderPayGrade]models.WeightAllotment); ok { + r0 = rf(appCtx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[internalmessages.OrderPayGrade]models.WeightAllotment) + } + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext) error); ok { + r1 = rf(appCtx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetWeightAllotment provides a mock function with given fields: appCtx, grade, ordersType +func (_m *WeightAllotmentFetcher) GetWeightAllotment(appCtx appcontext.AppContext, grade string, ordersType internalmessages.OrdersType) (models.WeightAllotment, error) { + ret := _m.Called(appCtx, grade, ordersType) + + if len(ret) == 0 { + panic("no return value specified for GetWeightAllotment") + } + + var r0 models.WeightAllotment + var r1 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, internalmessages.OrdersType) (models.WeightAllotment, error)); ok { + return rf(appCtx, grade, ordersType) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, internalmessages.OrdersType) models.WeightAllotment); ok { + r0 = rf(appCtx, grade, ordersType) + } else { + r0 = ret.Get(0).(models.WeightAllotment) + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, string, internalmessages.OrdersType) error); ok { + r1 = rf(appCtx, grade, ordersType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetWeightAllotmentByOrdersType provides a mock function with given fields: appCtx, ordersType +func (_m *WeightAllotmentFetcher) GetWeightAllotmentByOrdersType(appCtx appcontext.AppContext, ordersType internalmessages.OrdersType) (models.WeightAllotment, error) { + ret := _m.Called(appCtx, ordersType) + + if len(ret) == 0 { + panic("no return value specified for GetWeightAllotmentByOrdersType") + } + + var r0 models.WeightAllotment + var r1 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, internalmessages.OrdersType) (models.WeightAllotment, error)); ok { + return rf(appCtx, ordersType) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, internalmessages.OrdersType) models.WeightAllotment); ok { + r0 = rf(appCtx, ordersType) + } else { + r0 = ret.Get(0).(models.WeightAllotment) + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, internalmessages.OrdersType) error); ok { + r1 = rf(appCtx, ordersType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewWeightAllotmentFetcher creates a new instance of WeightAllotmentFetcher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewWeightAllotmentFetcher(t interface { + mock.TestingT + Cleanup(func()) +}) *WeightAllotmentFetcher { + mock := &WeightAllotmentFetcher{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/services/mocks/WeightRestrictor.go b/pkg/services/mocks/WeightRestrictor.go new file mode 100644 index 00000000000..6f7ad72bae4 --- /dev/null +++ b/pkg/services/mocks/WeightRestrictor.go @@ -0,0 +1,89 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + appcontext "github.com/transcom/mymove/pkg/appcontext" + + models "github.com/transcom/mymove/pkg/models" +) + +// WeightRestrictor is an autogenerated mock type for the WeightRestrictor type +type WeightRestrictor struct { + mock.Mock +} + +// ApplyWeightRestrictionToEntitlement provides a mock function with given fields: appCtx, entitlement, weightRestriction, eTag +func (_m *WeightRestrictor) ApplyWeightRestrictionToEntitlement(appCtx appcontext.AppContext, entitlement models.Entitlement, weightRestriction int, eTag string) (*models.Entitlement, error) { + ret := _m.Called(appCtx, entitlement, weightRestriction, eTag) + + if len(ret) == 0 { + panic("no return value specified for ApplyWeightRestrictionToEntitlement") + } + + var r0 *models.Entitlement + var r1 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.Entitlement, int, string) (*models.Entitlement, error)); ok { + return rf(appCtx, entitlement, weightRestriction, eTag) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.Entitlement, int, string) *models.Entitlement); ok { + r0 = rf(appCtx, entitlement, weightRestriction, eTag) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*models.Entitlement) + } + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, models.Entitlement, int, string) error); ok { + r1 = rf(appCtx, entitlement, weightRestriction, eTag) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RemoveWeightRestrictionFromEntitlement provides a mock function with given fields: appCtx, entitlement, eTag +func (_m *WeightRestrictor) RemoveWeightRestrictionFromEntitlement(appCtx appcontext.AppContext, entitlement models.Entitlement, eTag string) (*models.Entitlement, error) { + ret := _m.Called(appCtx, entitlement, eTag) + + if len(ret) == 0 { + panic("no return value specified for RemoveWeightRestrictionFromEntitlement") + } + + var r0 *models.Entitlement + var r1 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.Entitlement, string) (*models.Entitlement, error)); ok { + return rf(appCtx, entitlement, eTag) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.Entitlement, string) *models.Entitlement); ok { + r0 = rf(appCtx, entitlement, eTag) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*models.Entitlement) + } + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, models.Entitlement, string) error); ok { + r1 = rf(appCtx, entitlement, eTag) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewWeightRestrictor creates a new instance of WeightRestrictor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewWeightRestrictor(t interface { + mock.TestingT + Cleanup(func()) +}) *WeightRestrictor { + mock := &WeightRestrictor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 6e0a061eb216c8e1390c888353c3f7f7fe765304 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Wed, 5 Feb 2025 15:33:00 +0000 Subject: [PATCH 10/15] added order_fetcher tests --- pkg/services/order/order_fetcher_test.go | 404 +++++++++++++++++++++++ 1 file changed, 404 insertions(+) diff --git a/pkg/services/order/order_fetcher_test.go b/pkg/services/order/order_fetcher_test.go index 57e85401246..653d6d7d129 100644 --- a/pkg/services/order/order_fetcher_test.go +++ b/pkg/services/order/order_fetcher_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/gofrs/uuid" + "github.com/stretchr/testify/mock" "github.com/transcom/mymove/pkg/auth" "github.com/transcom/mymove/pkg/factory" @@ -12,6 +13,7 @@ import ( "github.com/transcom/mymove/pkg/models/roles" "github.com/transcom/mymove/pkg/services" "github.com/transcom/mymove/pkg/services/entitlements" + "github.com/transcom/mymove/pkg/services/mocks" moveservice "github.com/transcom/mymove/pkg/services/move" officeuserservice "github.com/transcom/mymove/pkg/services/office_user" "github.com/transcom/mymove/pkg/testdatagen" @@ -2307,3 +2309,405 @@ func (suite *OrderServiceSuite) TestOriginDutyLocationFilter() { suite.Equal(locationName, string(expectedMoves[0].Orders.OriginDutyLocation.Name)) }) } + +func (suite *OrderServiceSuite) TestListDestinationRequestsOrders() { + army := models.AffiliationARMY + airForce := models.AffiliationAIRFORCE + spaceForce := models.AffiliationSPACEFORCE + usmc := models.AffiliationMARINES + + setupTestData := func(officeUserGBLOC string) (models.OfficeUser, auth.Session) { + + officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{ + { + Model: models.TransportationOffice{ + Gbloc: officeUserGBLOC, + }, + }, + }, []roles.RoleType{roles.RoleTypeTOO}) + + factory.FetchOrBuildPostalCodeToGBLOC(suite.DB(), "99501", officeUser.TransportationOffice.Gbloc) + + fetcher := &mocks.OfficeUserGblocFetcher{} + fetcher.On("FetchGblocForOfficeUser", + mock.AnythingOfType("*appcontext.appContext"), + officeUser.ID, + ).Return(officeUserGBLOC, nil) + + session := auth.Session{ + ApplicationName: auth.OfficeApp, + Roles: officeUser.User.Roles, + OfficeUserID: officeUser.ID, + IDToken: "fake_token", + AccessToken: "fakeAccessToken", + } + + return officeUser, session + } + + buildMoveKKFA := func() (models.Move, models.MTOShipment) { + postalCode := "90210" + factory.FetchOrBuildPostalCodeToGBLOC(suite.DB(), "90210", "KKFA") + + // setting up two moves, each with requested destination SIT service items + destinationAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{PostalCode: postalCode}, + }, + }, nil) + + move := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + Status: models.MoveStatusAPPROVALSREQUESTED, + Show: models.BoolPointer(true), + }, + }}, nil) + + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + Status: models.MTOShipmentStatusApproved, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: destinationAddress, + LinkOnly: true, + }, + }, nil) + + return move, shipment + } + + buildMoveZone2AK := func(branch models.ServiceMemberAffiliation) (models.Move, models.MTOShipment) { + // Create a USAF move in Alaska Zone II + // this is a hard coded uuid that is a us_post_region_cities_id within AK Zone II + zone2UUID, err := uuid.FromString("66768964-e0de-41f3-b9be-7ef32e4ae2b4") + suite.FatalNoError(err) + destinationAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + City: "Anchorage", + State: "AK", + PostalCode: "99501", + UsPostRegionCityID: &zone2UUID, + }, + }, + }, nil) + + // setting up two moves, each with requested destination SIT service items + move := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + Status: models.MoveStatusAPPROVALSREQUESTED, + Show: models.BoolPointer(true), + }, + }, + { + Model: models.ServiceMember{ + Affiliation: &branch, + }, + }, + }, nil) + + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + Status: models.MTOShipmentStatusApproved, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: destinationAddress, + LinkOnly: true, + }, + }, nil) + + return move, shipment + } + + buildMoveZone4AK := func(branch models.ServiceMemberAffiliation) (models.Move, models.MTOShipment) { + // Create a USAF move in Alaska Zone II + // this is a hard coded uuid that is a us_post_region_cities_id within AK Zone II + zone4UUID, err := uuid.FromString("78a6f230-9a3a-46ed-aa48-2e3decfe70ff") + suite.FatalNoError(err) + destinationAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + City: "Anchorage", + State: "AK", + PostalCode: "99501", + UsPostRegionCityID: &zone4UUID, + }, + }, + }, nil) + // setting up two moves, each with requested destination SIT service items + move := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + Status: models.MoveStatusAPPROVALSREQUESTED, + Show: models.BoolPointer(true), + }, + }, + { + Model: models.ServiceMember{ + Affiliation: &branch, + }, + }, + }, nil) + + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + Status: models.MTOShipmentStatusApproved, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: destinationAddress, + LinkOnly: true, + }, + }, nil) + + return move, shipment + } + + waf := entitlements.NewWeightAllotmentFetcher() + orderFetcher := NewOrderFetcher(waf) + + suite.Run("returns moves for KKFA GBLOC when destination address is in KKFA GBLOC", func() { + officeUser, session := setupTestData("KKFA") + // setting up two moves, each with requested destination SIT service items + move, shipment := buildMoveKKFA() + + // destination service item in SUBMITTED status + factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeDDFSIT, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: shipment, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + }, nil) + + move2, shipment2 := buildMoveKKFA() + + // destination shuttle + factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeDDSHUT, + }, + }, + { + Model: move2, + LinkOnly: true, + }, + { + Model: shipment2, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + }, nil) + + moves, moveCount, err := orderFetcher.ListDestinationRequestsOrders( + suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, &services.ListOrderParams{}, + ) + + suite.FatalNoError(err) + suite.Equal(2, moveCount) + suite.Len(moves, 2) + }) + + suite.Run("returns moves for MBFL GBLOC including USAF/SF in Alaska Zone II", func() { + officeUser, session := setupTestData("MBFL") + + // setting up two moves, each with requested destination SIT service items + // a move associated with an air force customer containing AK Zone II shipment + move, shipment := buildMoveZone2AK(airForce) + + // destination service item in SUBMITTED status + factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeDDFSIT, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: shipment, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + }, nil) + + // Create a move outside Alaska Zone II (Zone IV in this case) + move2, shipment2 := buildMoveZone4AK(spaceForce) + + // destination service item in SUBMITTED status + factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeDDFSIT, + }, + }, + { + Model: move2, + LinkOnly: true, + }, + { + Model: shipment2, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + }, nil) + + params := services.ListOrderParams{Status: []string{string(models.MoveStatusAPPROVALSREQUESTED)}} + moves, moveCount, err := orderFetcher.ListDestinationRequestsOrders( + suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, ¶ms, + ) + + // we should get both moves back because one is in Zone II & the other is within the postal code GBLOC + suite.FatalNoError(err) + suite.Equal(2, moveCount) + suite.Len(moves, 2) + }) + + suite.Run("returns moves for JEAT GBLOC excluding USAF/SF in Alaska Zone II", func() { + officeUser, session := setupTestData("JEAT") + + // Create a move in Zone II, but not an air force or space force service member + move, shipment := buildMoveZone4AK(army) + + // destination service item in SUBMITTED status + factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeDDFSIT, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: shipment, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + }, nil) + + moves, moveCount, err := orderFetcher.ListDestinationRequestsOrders( + suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, &services.ListOrderParams{}, + ) + + suite.FatalNoError(err) + suite.Equal(1, moveCount) + suite.Len(moves, 1) + }) + + suite.Run("returns moves for USMC GBLOC when moves belong to USMC servicemembers", func() { + officeUser, session := setupTestData("USMC") + + // setting up two moves, each with requested destination SIT service items + // both will be USMC moves, one in Zone II AK and the other not + move, shipment := buildMoveZone2AK(usmc) + + // destination service item in SUBMITTED status + factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeDDFSIT, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: shipment, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + }, nil) + + // this one won't be in Zone II + move2, shipment2 := buildMoveZone4AK(usmc) + + // destination shuttle + factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeDDSHUT, + }, + }, + { + Model: move2, + LinkOnly: true, + }, + { + Model: shipment2, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + }, nil) + + moves, moveCount, err := orderFetcher.ListDestinationRequestsOrders( + suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, &services.ListOrderParams{}, + ) + + // we should get both moves back since they're USMC moves and zone doesn't matter + suite.FatalNoError(err) + suite.Equal(2, moveCount) + suite.Len(moves, 2) + }) +} From f680a0bba5e4ab9ab082db28c95e0671e062b776 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Wed, 5 Feb 2025 20:28:35 +0000 Subject: [PATCH 11/15] added test for handler --- pkg/handlers/ghcapi/queues_test.go | 141 +++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/pkg/handlers/ghcapi/queues_test.go b/pkg/handlers/ghcapi/queues_test.go index 4711af0de0a..6de9b13dd91 100644 --- a/pkg/handlers/ghcapi/queues_test.go +++ b/pkg/handlers/ghcapi/queues_test.go @@ -1893,3 +1893,144 @@ func (suite *HandlerSuite) TestGetBulkAssignmentDataHandler() { suite.Len(payload.BulkAssignmentMoveIDs, 1) }) } + +func (suite *HandlerSuite) TestGetDestinationRequestsQueuesHandler() { + waf := entitlements.NewWeightAllotmentFetcher() + // default GBLOC is KKFA + officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), factory.GetTraitActiveOfficeUser(), []roles.RoleType{roles.RoleTypeTOO}) + officeUser.User.Roles = append(officeUser.User.Roles, roles.Role{ + RoleType: roles.RoleTypeTOO, + }) + + postalCode := "90210" + postalCode2 := "73064" + factory.FetchOrBuildPostalCodeToGBLOC(suite.DB(), "90210", "KKFA") + factory.FetchOrBuildPostalCodeToGBLOC(suite.DB(), "73064", "JEAT") + + // setting up two moves, one we will see and the other we won't + move := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + Status: models.MoveStatusAPPROVALSREQUESTED, + Show: models.BoolPointer(true), + }, + }}, nil) + + destinationAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{PostalCode: postalCode}, + }, + }, nil) + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + Status: models.MTOShipmentStatusApproved, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: destinationAddress, + LinkOnly: true, + }, + }, nil) + + // destination service item in SUBMITTED status + factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeDDFSIT, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: shipment, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + }, nil) + + move2 := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + Status: models.MoveStatusAPPROVALSREQUESTED, + Show: models.BoolPointer(true), + }, + }}, nil) + + destinationAddress2 := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{PostalCode: postalCode2}, + }, + }, nil) + shipment2 := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + Status: models.MTOShipmentStatusApproved, + }, + }, + { + Model: move2, + LinkOnly: true, + }, + { + Model: destinationAddress2, + LinkOnly: true, + }, + }, nil) + + // destination shuttle + factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeDDSHUT, + }, + }, + { + Model: move2, + LinkOnly: true, + }, + { + Model: shipment2, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + }, nil) + + request := httptest.NewRequest("GET", "/queues/destination-requests", nil) + request = suite.AuthenticateOfficeRequest(request, officeUser) + params := queues.GetDestinationRequestsQueueParams{ + HTTPRequest: request, + } + handlerConfig := suite.HandlerConfig() + mockUnlocker := movelocker.NewMoveUnlocker() + handler := GetDestinationRequestsQueueHandler{ + handlerConfig, + order.NewOrderFetcher(waf), + mockUnlocker, + officeusercreator.NewOfficeUserFetcherPop(), + } + + response := handler.Handle(params) + suite.IsNotErrResponse(response) + suite.IsType(&queues.GetDestinationRequestsQueueOK{}, response) + payload := response.(*queues.GetDestinationRequestsQueueOK).Payload + + // should only have one move + result := payload.QueueMoves[0] + suite.Len(payload.QueueMoves, 1) + suite.Equal(move.ID.String(), result.ID.String()) +} From ce1423e6dbb3d3be8f22d7b41ce2f460659ee901 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Thu, 6 Feb 2025 16:07:25 +0000 Subject: [PATCH 12/15] added address update to test --- pkg/services/order/order_fetcher_test.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pkg/services/order/order_fetcher_test.go b/pkg/services/order/order_fetcher_test.go index 653d6d7d129..0e4a26b72bd 100644 --- a/pkg/services/order/order_fetcher_test.go +++ b/pkg/services/order/order_fetcher_test.go @@ -2536,13 +2536,25 @@ func (suite *OrderServiceSuite) TestListDestinationRequestsOrders() { }, }, nil) + move3, shipment3 := buildMoveKKFA() + factory.BuildShipmentAddressUpdate(suite.DB(), []factory.Customization{ + { + Model: shipment3, + LinkOnly: true, + }, + { + Model: move3, + LinkOnly: true, + }, + }, []factory.Trait{factory.GetTraitShipmentAddressUpdateRequested}) + moves, moveCount, err := orderFetcher.ListDestinationRequestsOrders( suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, &services.ListOrderParams{}, ) suite.FatalNoError(err) - suite.Equal(2, moveCount) - suite.Len(moves, 2) + suite.Equal(3, moveCount) + suite.Len(moves, 3) }) suite.Run("returns moves for MBFL GBLOC including USAF/SF in Alaska Zone II", func() { From 4fedc408d199b1d37b7751f2c22e739906779911 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Fri, 7 Feb 2025 15:27:22 +0000 Subject: [PATCH 13/15] query improvements, updating csv file name prefix --- ...stination_queue_db_func_and_gbloc_view.up.sql | 16 ++++++++-------- src/pages/Office/MoveQueue/MoveQueue.jsx | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql b/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql index 8aabdaeddf0..a17a02d5644 100644 --- a/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql +++ b/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql @@ -351,20 +351,20 @@ BEGIN )::JSONB AS too_assigned, COUNT(*) OVER() AS total_count FROM moves - INNER JOIN orders ON moves.orders_id = orders.id - LEFT JOIN mto_shipments ON mto_shipments.move_id = moves.id + JOIN orders ON moves.orders_id = orders.id + JOIN mto_shipments ON mto_shipments.move_id = moves.id LEFT JOIN ppm_shipments ON ppm_shipments.shipment_id = mto_shipments.id - LEFT JOIN mto_service_items ON mto_shipments.id = mto_service_items.mto_shipment_id - LEFT JOIN re_services ON mto_service_items.re_service_id = re_services.id - LEFT JOIN service_members ON orders.service_member_id = service_members.id - LEFT JOIN duty_locations AS new_duty_locations ON orders.new_duty_location_id = new_duty_locations.id - LEFT JOIN duty_locations AS origin_duty_locations ON orders.origin_duty_location_id = origin_duty_locations.id + JOIN mto_service_items ON mto_shipments.id = mto_service_items.mto_shipment_id + JOIN re_services ON mto_service_items.re_service_id = re_services.id + JOIN service_members ON orders.service_member_id = service_members.id + JOIN duty_locations AS new_duty_locations ON orders.new_duty_location_id = new_duty_locations.id + JOIN duty_locations AS origin_duty_locations ON orders.origin_duty_location_id = origin_duty_locations.id LEFT JOIN office_users AS too_user ON moves.too_assigned_id = too_user.id LEFT JOIN office_users AS locked_user ON moves.locked_by = locked_user.id LEFT JOIN transportation_offices AS counseling_offices ON moves.counseling_transportation_office_id = counseling_offices.id LEFT JOIN shipment_address_updates ON shipment_address_updates.shipment_id = mto_shipments.id - LEFT JOIN move_to_dest_gbloc ON move_to_dest_gbloc.move_id = moves.id + JOIN move_to_dest_gbloc ON move_to_dest_gbloc.move_id = moves.id WHERE moves.show = TRUE '; diff --git a/src/pages/Office/MoveQueue/MoveQueue.jsx b/src/pages/Office/MoveQueue/MoveQueue.jsx index e0c08868603..36e090a48de 100644 --- a/src/pages/Office/MoveQueue/MoveQueue.jsx +++ b/src/pages/Office/MoveQueue/MoveQueue.jsx @@ -369,9 +369,9 @@ const MoveQueue = ({ isQueueManagementFFEnabled, userPrivileges, isBulkAssignmen handleClick={handleClick} useQueries={useDestinationRequestsQueueQueries} showCSVExport - csvExportFileNamePrefix="Task-Order-Queue" + csvExportFileNamePrefix="Destination-Requests-Queue" csvExportQueueFetcher={getDestinationRequestsQueue} - csvExportQueueFetcherKey="queueMoves" + csvExportQueueFetcherKey="destinationQueueMoves" sessionStorageKey={queueType} key={queueType} /> From f4ce771de883deb5e929e3571a847792ff56938e Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Fri, 7 Feb 2025 16:09:22 +0000 Subject: [PATCH 14/15] test adjustments --- pkg/services/order/order_fetcher_test.go | 60 ++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/pkg/services/order/order_fetcher_test.go b/pkg/services/order/order_fetcher_test.go index 0e4a26b72bd..f597146e48e 100644 --- a/pkg/services/order/order_fetcher_test.go +++ b/pkg/services/order/order_fetcher_test.go @@ -2537,6 +2537,26 @@ func (suite *OrderServiceSuite) TestListDestinationRequestsOrders() { }, nil) move3, shipment3 := buildMoveKKFA() + factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeMS, + }, + }, + { + Model: move3, + LinkOnly: true, + }, + { + Model: shipment3, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusApproved, + }, + }, + }, nil) factory.BuildShipmentAddressUpdate(suite.DB(), []factory.Customization{ { Model: shipment3, @@ -2713,13 +2733,47 @@ func (suite *OrderServiceSuite) TestListDestinationRequestsOrders() { }, }, nil) + move3, shipment3 := buildMoveZone4AK(usmc) + // we need to create a service item and attach it to the move/shipment + // else the query will exclude the move since it doesn't use LEFT JOINs + factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeMS, + }, + }, + { + Model: move3, + LinkOnly: true, + }, + { + Model: shipment3, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusApproved, + }, + }, + }, nil) + factory.BuildShipmentAddressUpdate(suite.DB(), []factory.Customization{ + { + Model: shipment3, + LinkOnly: true, + }, + { + Model: move3, + LinkOnly: true, + }, + }, []factory.Trait{factory.GetTraitShipmentAddressUpdateRequested}) + moves, moveCount, err := orderFetcher.ListDestinationRequestsOrders( suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, &services.ListOrderParams{}, ) - // we should get both moves back since they're USMC moves and zone doesn't matter + // we should get three moves back since they're USMC moves and zone doesn't matter suite.FatalNoError(err) - suite.Equal(2, moveCount) - suite.Len(moves, 2) + suite.Equal(3, moveCount) + suite.Len(moves, 3) }) } From e38c8634c05337d31689262df4687df01a173859 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Fri, 7 Feb 2025 18:09:16 +0000 Subject: [PATCH 15/15] updating to include moves.show in return --- ...3173216_add_destination_queue_db_func_and_gbloc_view.up.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql b/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql index a17a02d5644..5148f0f57d7 100644 --- a/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql +++ b/migrations/app/schema/20250123173216_add_destination_queue_db_func_and_gbloc_view.up.sql @@ -266,6 +266,7 @@ CREATE OR REPLACE FUNCTION get_destination_queue( ) RETURNS TABLE ( id UUID, + show BOOLEAN, locator TEXT, submitted_at TIMESTAMP WITH TIME ZONE, orders_id UUID, @@ -299,6 +300,7 @@ BEGIN sql_query := ' SELECT moves.id AS id, + moves.show AS show, moves.locator::TEXT AS locator, moves.submitted_at::TIMESTAMP WITH TIME ZONE AS submitted_at, moves.orders_id AS orders_id, @@ -467,6 +469,7 @@ BEGIN sql_query := sql_query || ' GROUP BY moves.id, + moves.show, moves.locator, moves.submitted_at, moves.orders_id,