From 33a5ac0ee66390483373a2bc845a66c90e01e8f8 Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Wed, 22 Jan 2025 17:35:40 +0000 Subject: [PATCH 01/26] initial --- pkg/gen/primeapi/embedded_spec.go | 364 ++++++- pkg/gen/primemessages/m_t_o_service_item.go | 12 + ...o_service_item_international_dest_s_i_t.go | 987 ++++++++++++++++++ ...service_item_international_origin_s_i_t.go | 900 ++++++++++++++++ .../m_t_o_service_item_model_type.go | 10 +- .../update_m_t_o_service_item_model_type.go | 8 + .../update_m_t_o_service_item_s_i_t.go | 8 +- pkg/gen/primev2api/embedded_spec.go | 8 +- .../m_t_o_service_item_model_type.go | 10 +- pkg/gen/primev3api/embedded_spec.go | 8 +- .../m_t_o_service_item_model_type.go | 10 +- pkg/handlers/primeapi/mto_service_item.go | 12 +- .../primeapi/payloads/payload_to_model.go | 147 +++ .../primeapiv3/payloads/model_to_payload.go | 57 + pkg/models/re_service.go | 4 + .../mto_service_item_creator.go | 106 +- .../mto_service_item_validators.go | 8 + .../sit_entry_date_updater.go | 13 +- .../ServiceItemDetails/ServiceItemDetails.jsx | 98 +- .../ServiceItemsTable/ServiceItemsTable.jsx | 2 +- .../CreateShipmentServiceItemForm.jsx | 14 + ...rnationalDestinationSITServiceItemForm.jsx | 133 +++ .../InternationalOriginSITServiceItemForm.jsx | 110 ++ src/constants/prime.js | 2 + src/constants/serviceItems.js | 29 + src/constants/sitUpdates.js | 10 +- .../PrimeUIUpdateInternationalDestSITForm.jsx | 103 ++ ...rimeUIUpdateInternationalOriginSITForm.jsx | 103 ++ .../PrimeUIUpdateSitServiceItem.jsx | 17 + .../MTOServiceItemInternationalDestSIT.yaml | 74 ++ .../MTOServiceItemInternationalOriginSIT.yaml | 50 + .../prime/MTOServiceItemModelType.yaml | 4 + swagger-def/prime.yaml | 20 + swagger/prime.yaml | 168 +++ swagger/prime_v2.yaml | 4 + swagger/prime_v3.yaml | 4 + 36 files changed, 3561 insertions(+), 56 deletions(-) create mode 100644 pkg/gen/primemessages/m_t_o_service_item_international_dest_s_i_t.go create mode 100644 pkg/gen/primemessages/m_t_o_service_item_international_origin_s_i_t.go create mode 100644 src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.jsx create mode 100644 src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.jsx create mode 100644 src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalDestSITForm.jsx create mode 100644 src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalOriginSITForm.jsx create mode 100644 swagger-def/definitions/prime/MTOServiceItemInternationalDestSIT.yaml create mode 100644 swagger-def/definitions/prime/MTOServiceItemInternationalOriginSIT.yaml diff --git a/pkg/gen/primeapi/embedded_spec.go b/pkg/gen/primeapi/embedded_spec.go index 5ce22a16bfb..f176b90dda0 100644 --- a/pkg/gen/primeapi/embedded_spec.go +++ b/pkg/gen/primeapi/embedded_spec.go @@ -2358,6 +2358,102 @@ func init() { } ] }, + "MTOServiceItemInternationalDestSIT": { + "description": "Describes a international destination SIT service item. Subtype of a MTOServiceItem.", + "allOf": [ + { + "$ref": "#/definitions/MTOServiceItem" + }, + { + "type": "object", + "required": [ + "reServiceCode", + "sitEntryDate", + "reason" + ], + "properties": { + "dateOfContact1": { + "description": "Date of attempted contact by the prime corresponding to ` + "`" + `timeMilitary1` + "`" + `.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "dateOfContact2": { + "description": "Date of attempted contact by the prime corresponding to ` + "`" + `timeMilitary2` + "`" + `.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "firstAvailableDeliveryDate1": { + "description": "First available date that Prime can deliver SIT service item.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "firstAvailableDeliveryDate2": { + "description": "Second available date that Prime can deliver SIT service item.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "reServiceCode": { + "description": "Service code allowed for this model type.", + "type": "string", + "enum": [ + "IDFSIT", + "IDASIT" + ] + }, + "reason": { + "description": "The reason item has been placed in SIT.\n", + "type": "string", + "x-nullable": true, + "x-omitempty": false + }, + "sitCustomerContacted": { + "description": "Date when the customer contacted the prime for a delivery out of SIT.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "sitDepartureDate": { + "description": "Departure date for SIT. This is the end date of the SIT at either origin or destination. This is optional as it can be updated using the UpdateMTOServiceItemSIT modelType at a later date.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "sitDestinationFinalAddress": { + "$ref": "#/definitions/Address" + }, + "sitEntryDate": { + "description": "Entry date for the SIT", + "type": "string", + "format": "date" + }, + "sitRequestedDelivery": { + "description": "Date when the customer has requested delivery out of SIT.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "timeMilitary1": { + "description": "Time of attempted contact corresponding to ` + "`" + `dateOfContact1` + "`" + `, in military format.", + "type": "string", + "pattern": "\\d{4}Z", + "x-nullable": true, + "example": "1400Z" + }, + "timeMilitary2": { + "description": "Time of attempted contact corresponding to ` + "`" + `dateOfContact2` + "`" + `, in military format.", + "type": "string", + "pattern": "\\d{4}Z", + "x-nullable": true, + "example": "1400Z" + } + } + } + ] + }, "MTOServiceItemInternationalFuelSurcharge": { "description": "Describes a international Port of Embarkation/Debarkation fuel surcharge service item subtype of a MTOServiceItem.", "allOf": [ @@ -2383,13 +2479,85 @@ func init() { } ] }, + "MTOServiceItemInternationalOriginSIT": { + "description": "Describes a international origin SIT service item. Subtype of a MTOServiceItem.", + "allOf": [ + { + "$ref": "#/definitions/MTOServiceItem" + }, + { + "type": "object", + "required": [ + "reServiceCode", + "reason", + "sitPostalCode", + "sitEntryDate" + ], + "properties": { + "reServiceCode": { + "description": "Service code allowed for this model type.", + "type": "string", + "enum": [ + "IOFSIT", + "IOASIT" + ] + }, + "reason": { + "description": "Explanation of why Prime is picking up SIT item.", + "type": "string", + "example": "Storage items need to be picked up" + }, + "requestApprovalsRequestedStatus": { + "type": "boolean" + }, + "sitCustomerContacted": { + "description": "Date when the customer contacted the prime for a delivery out of SIT.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "sitDepartureDate": { + "description": "Departure date for SIT. This is the end date of the SIT at either origin or destination. This is optional as it can be updated using the UpdateMTOServiceItemSIT modelType at a later date.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "sitEntryDate": { + "description": "Entry date for the SIT", + "type": "string", + "format": "date" + }, + "sitHHGActualOrigin": { + "$ref": "#/definitions/Address" + }, + "sitHHGOriginalOrigin": { + "$ref": "#/definitions/Address" + }, + "sitPostalCode": { + "type": "string", + "format": "zip", + "pattern": "^(\\d{5}([\\-]\\d{4})?)$", + "example": "90210" + }, + "sitRequestedDelivery": { + "description": "Date when the customer has requested delivery out of SIT.", + "type": "string", + "format": "date", + "x-nullable": true + } + } + } + ] + }, "MTOServiceItemModelType": { - "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", + "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT\n * IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "MTOServiceItemBasic", "MTOServiceItemOriginSIT", "MTOServiceItemDestSIT", + "MTOServiceItemInternationalOriginSIT", + "MTOServiceItemInternationalDestSIT", "MTOServiceItemShuttle", "MTOServiceItemDomesticCrating", "MTOServiceItemInternationalCrating", @@ -3981,7 +4149,7 @@ func init() { ] }, "UpdateMTOServiceItemModelType": { - "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DDFSIT - UpdateMTOServiceItemSIT\n * DDASIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * DOSFSC - UpdateMTOServiceItemSIT\n * DDSFSC - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * PODFSC - UpdateMTOServiceItemInternationalPortFSC\n * POEFSC - UpdateMTOServiceItemInternationalPortFSC\n\nThe documentation will then update with the supported fields.\n", + "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DDFSIT - UpdateMTOServiceItemSIT\n * DDASIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * DOSFSC - UpdateMTOServiceItemSIT\n * DDSFSC - UpdateMTOServiceItemSIT\n * IDDSIT - UpdateMTOServiceItemSIT\n * IDFSIT - UpdateMTOServiceItemSIT\n * IDASIT - UpdateMTOServiceItemSIT\n * IOPSIT - UpdateMTOServiceItemSIT\n * IOASIT - UpdateMTOServiceItemSIT\n * IOFSIT - UpdateMTOServiceItemSIT\n * IOSFSC - UpdateMTOServiceItemSIT\n * IDSFSC - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * PODFSC - UpdateMTOServiceItemInternationalPortFSC\n * POEFSC - UpdateMTOServiceItemInternationalPortFSC\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "UpdateMTOServiceItemSIT", @@ -4033,7 +4201,15 @@ func init() { "DOPSIT", "DOASIT", "DOFSIT", - "DOSFSC" + "DOSFSC", + "IDDSIT", + "IDASIT", + "IDFSIT", + "IDSFSC", + "IOPSIT", + "IOASIT", + "IOFSIT", + "IOSFSC" ] }, "requestApprovalsRequestedStatus": { @@ -7277,6 +7453,102 @@ func init() { } ] }, + "MTOServiceItemInternationalDestSIT": { + "description": "Describes a international destination SIT service item. Subtype of a MTOServiceItem.", + "allOf": [ + { + "$ref": "#/definitions/MTOServiceItem" + }, + { + "type": "object", + "required": [ + "reServiceCode", + "sitEntryDate", + "reason" + ], + "properties": { + "dateOfContact1": { + "description": "Date of attempted contact by the prime corresponding to ` + "`" + `timeMilitary1` + "`" + `.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "dateOfContact2": { + "description": "Date of attempted contact by the prime corresponding to ` + "`" + `timeMilitary2` + "`" + `.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "firstAvailableDeliveryDate1": { + "description": "First available date that Prime can deliver SIT service item.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "firstAvailableDeliveryDate2": { + "description": "Second available date that Prime can deliver SIT service item.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "reServiceCode": { + "description": "Service code allowed for this model type.", + "type": "string", + "enum": [ + "IDFSIT", + "IDASIT" + ] + }, + "reason": { + "description": "The reason item has been placed in SIT.\n", + "type": "string", + "x-nullable": true, + "x-omitempty": false + }, + "sitCustomerContacted": { + "description": "Date when the customer contacted the prime for a delivery out of SIT.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "sitDepartureDate": { + "description": "Departure date for SIT. This is the end date of the SIT at either origin or destination. This is optional as it can be updated using the UpdateMTOServiceItemSIT modelType at a later date.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "sitDestinationFinalAddress": { + "$ref": "#/definitions/Address" + }, + "sitEntryDate": { + "description": "Entry date for the SIT", + "type": "string", + "format": "date" + }, + "sitRequestedDelivery": { + "description": "Date when the customer has requested delivery out of SIT.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "timeMilitary1": { + "description": "Time of attempted contact corresponding to ` + "`" + `dateOfContact1` + "`" + `, in military format.", + "type": "string", + "pattern": "\\d{4}Z", + "x-nullable": true, + "example": "1400Z" + }, + "timeMilitary2": { + "description": "Time of attempted contact corresponding to ` + "`" + `dateOfContact2` + "`" + `, in military format.", + "type": "string", + "pattern": "\\d{4}Z", + "x-nullable": true, + "example": "1400Z" + } + } + } + ] + }, "MTOServiceItemInternationalFuelSurcharge": { "description": "Describes a international Port of Embarkation/Debarkation fuel surcharge service item subtype of a MTOServiceItem.", "allOf": [ @@ -7302,13 +7574,85 @@ func init() { } ] }, + "MTOServiceItemInternationalOriginSIT": { + "description": "Describes a international origin SIT service item. Subtype of a MTOServiceItem.", + "allOf": [ + { + "$ref": "#/definitions/MTOServiceItem" + }, + { + "type": "object", + "required": [ + "reServiceCode", + "reason", + "sitPostalCode", + "sitEntryDate" + ], + "properties": { + "reServiceCode": { + "description": "Service code allowed for this model type.", + "type": "string", + "enum": [ + "IOFSIT", + "IOASIT" + ] + }, + "reason": { + "description": "Explanation of why Prime is picking up SIT item.", + "type": "string", + "example": "Storage items need to be picked up" + }, + "requestApprovalsRequestedStatus": { + "type": "boolean" + }, + "sitCustomerContacted": { + "description": "Date when the customer contacted the prime for a delivery out of SIT.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "sitDepartureDate": { + "description": "Departure date for SIT. This is the end date of the SIT at either origin or destination. This is optional as it can be updated using the UpdateMTOServiceItemSIT modelType at a later date.", + "type": "string", + "format": "date", + "x-nullable": true + }, + "sitEntryDate": { + "description": "Entry date for the SIT", + "type": "string", + "format": "date" + }, + "sitHHGActualOrigin": { + "$ref": "#/definitions/Address" + }, + "sitHHGOriginalOrigin": { + "$ref": "#/definitions/Address" + }, + "sitPostalCode": { + "type": "string", + "format": "zip", + "pattern": "^(\\d{5}([\\-]\\d{4})?)$", + "example": "90210" + }, + "sitRequestedDelivery": { + "description": "Date when the customer has requested delivery out of SIT.", + "type": "string", + "format": "date", + "x-nullable": true + } + } + } + ] + }, "MTOServiceItemModelType": { - "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", + "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT\n * IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "MTOServiceItemBasic", "MTOServiceItemOriginSIT", "MTOServiceItemDestSIT", + "MTOServiceItemInternationalOriginSIT", + "MTOServiceItemInternationalDestSIT", "MTOServiceItemShuttle", "MTOServiceItemDomesticCrating", "MTOServiceItemInternationalCrating", @@ -8905,7 +9249,7 @@ func init() { ] }, "UpdateMTOServiceItemModelType": { - "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DDFSIT - UpdateMTOServiceItemSIT\n * DDASIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * DOSFSC - UpdateMTOServiceItemSIT\n * DDSFSC - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * PODFSC - UpdateMTOServiceItemInternationalPortFSC\n * POEFSC - UpdateMTOServiceItemInternationalPortFSC\n\nThe documentation will then update with the supported fields.\n", + "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DDFSIT - UpdateMTOServiceItemSIT\n * DDASIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * DOSFSC - UpdateMTOServiceItemSIT\n * DDSFSC - UpdateMTOServiceItemSIT\n * IDDSIT - UpdateMTOServiceItemSIT\n * IDFSIT - UpdateMTOServiceItemSIT\n * IDASIT - UpdateMTOServiceItemSIT\n * IOPSIT - UpdateMTOServiceItemSIT\n * IOASIT - UpdateMTOServiceItemSIT\n * IOFSIT - UpdateMTOServiceItemSIT\n * IOSFSC - UpdateMTOServiceItemSIT\n * IDSFSC - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * PODFSC - UpdateMTOServiceItemInternationalPortFSC\n * POEFSC - UpdateMTOServiceItemInternationalPortFSC\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "UpdateMTOServiceItemSIT", @@ -8957,7 +9301,15 @@ func init() { "DOPSIT", "DOASIT", "DOFSIT", - "DOSFSC" + "DOSFSC", + "IDDSIT", + "IDASIT", + "IDFSIT", + "IDSFSC", + "IOPSIT", + "IOASIT", + "IOFSIT", + "IOSFSC" ] }, "requestApprovalsRequestedStatus": { diff --git a/pkg/gen/primemessages/m_t_o_service_item.go b/pkg/gen/primemessages/m_t_o_service_item.go index 1db2b84d144..952e0cd6262 100644 --- a/pkg/gen/primemessages/m_t_o_service_item.go +++ b/pkg/gen/primemessages/m_t_o_service_item.go @@ -279,12 +279,24 @@ func unmarshalMTOServiceItem(data []byte, consumer runtime.Consumer) (MTOService return nil, err } return &result, nil + case "MTOServiceItemInternationalDestSIT": + var result MTOServiceItemInternationalDestSIT + if err := consumer.Consume(buf2, &result); err != nil { + return nil, err + } + return &result, nil case "MTOServiceItemInternationalFuelSurcharge": var result MTOServiceItemInternationalFuelSurcharge if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil + case "MTOServiceItemInternationalOriginSIT": + var result MTOServiceItemInternationalOriginSIT + if err := consumer.Consume(buf2, &result); err != nil { + return nil, err + } + return &result, nil case "MTOServiceItemOriginSIT": var result MTOServiceItemOriginSIT if err := consumer.Consume(buf2, &result); err != nil { diff --git a/pkg/gen/primemessages/m_t_o_service_item_international_dest_s_i_t.go b/pkg/gen/primemessages/m_t_o_service_item_international_dest_s_i_t.go new file mode 100644 index 00000000000..07ab443e34d --- /dev/null +++ b/pkg/gen/primemessages/m_t_o_service_item_international_dest_s_i_t.go @@ -0,0 +1,987 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package primemessages + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "bytes" + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// MTOServiceItemInternationalDestSIT Describes a international destination SIT service item. Subtype of a MTOServiceItem. +// +// swagger:model MTOServiceItemInternationalDestSIT +type MTOServiceItemInternationalDestSIT struct { + eTagField string + + idField strfmt.UUID + + lockedPriceCentsField *int64 + + moveTaskOrderIdField *strfmt.UUID + + mtoShipmentIdField strfmt.UUID + + reServiceNameField string + + rejectionReasonField *string + + serviceRequestDocumentsField ServiceRequestDocuments + + statusField MTOServiceItemStatus + + // Date of attempted contact by the prime corresponding to `timeMilitary1`. + // Format: date + DateOfContact1 *strfmt.Date `json:"dateOfContact1,omitempty"` + + // Date of attempted contact by the prime corresponding to `timeMilitary2`. + // Format: date + DateOfContact2 *strfmt.Date `json:"dateOfContact2,omitempty"` + + // First available date that Prime can deliver SIT service item. + // Format: date + FirstAvailableDeliveryDate1 *strfmt.Date `json:"firstAvailableDeliveryDate1,omitempty"` + + // Second available date that Prime can deliver SIT service item. + // Format: date + FirstAvailableDeliveryDate2 *strfmt.Date `json:"firstAvailableDeliveryDate2,omitempty"` + + // Service code allowed for this model type. + // Required: true + // Enum: [IDFSIT IDASIT] + ReServiceCode *string `json:"reServiceCode"` + + // The reason item has been placed in SIT. + // + // Required: true + Reason *string `json:"reason"` + + // Date when the customer contacted the prime for a delivery out of SIT. + // Format: date + SitCustomerContacted *strfmt.Date `json:"sitCustomerContacted,omitempty"` + + // Departure date for SIT. This is the end date of the SIT at either origin or destination. This is optional as it can be updated using the UpdateMTOServiceItemSIT modelType at a later date. + // Format: date + SitDepartureDate *strfmt.Date `json:"sitDepartureDate,omitempty"` + + // sit destination final address + SitDestinationFinalAddress *Address `json:"sitDestinationFinalAddress,omitempty"` + + // Entry date for the SIT + // Required: true + // Format: date + SitEntryDate *strfmt.Date `json:"sitEntryDate"` + + // Date when the customer has requested delivery out of SIT. + // Format: date + SitRequestedDelivery *strfmt.Date `json:"sitRequestedDelivery,omitempty"` + + // Time of attempted contact corresponding to `dateOfContact1`, in military format. + // Example: 1400Z + // Pattern: \d{4}Z + TimeMilitary1 *string `json:"timeMilitary1,omitempty"` + + // Time of attempted contact corresponding to `dateOfContact2`, in military format. + // Example: 1400Z + // Pattern: \d{4}Z + TimeMilitary2 *string `json:"timeMilitary2,omitempty"` +} + +// ETag gets the e tag of this subtype +func (m *MTOServiceItemInternationalDestSIT) ETag() string { + return m.eTagField +} + +// SetETag sets the e tag of this subtype +func (m *MTOServiceItemInternationalDestSIT) SetETag(val string) { + m.eTagField = val +} + +// ID gets the id of this subtype +func (m *MTOServiceItemInternationalDestSIT) ID() strfmt.UUID { + return m.idField +} + +// SetID sets the id of this subtype +func (m *MTOServiceItemInternationalDestSIT) SetID(val strfmt.UUID) { + m.idField = val +} + +// LockedPriceCents gets the locked price cents of this subtype +func (m *MTOServiceItemInternationalDestSIT) LockedPriceCents() *int64 { + return m.lockedPriceCentsField +} + +// SetLockedPriceCents sets the locked price cents of this subtype +func (m *MTOServiceItemInternationalDestSIT) SetLockedPriceCents(val *int64) { + m.lockedPriceCentsField = val +} + +// ModelType gets the model type of this subtype +func (m *MTOServiceItemInternationalDestSIT) ModelType() MTOServiceItemModelType { + return "MTOServiceItemInternationalDestSIT" +} + +// SetModelType sets the model type of this subtype +func (m *MTOServiceItemInternationalDestSIT) SetModelType(val MTOServiceItemModelType) { +} + +// MoveTaskOrderID gets the move task order ID of this subtype +func (m *MTOServiceItemInternationalDestSIT) MoveTaskOrderID() *strfmt.UUID { + return m.moveTaskOrderIdField +} + +// SetMoveTaskOrderID sets the move task order ID of this subtype +func (m *MTOServiceItemInternationalDestSIT) SetMoveTaskOrderID(val *strfmt.UUID) { + m.moveTaskOrderIdField = val +} + +// MtoShipmentID gets the mto shipment ID of this subtype +func (m *MTOServiceItemInternationalDestSIT) MtoShipmentID() strfmt.UUID { + return m.mtoShipmentIdField +} + +// SetMtoShipmentID sets the mto shipment ID of this subtype +func (m *MTOServiceItemInternationalDestSIT) SetMtoShipmentID(val strfmt.UUID) { + m.mtoShipmentIdField = val +} + +// ReServiceName gets the re service name of this subtype +func (m *MTOServiceItemInternationalDestSIT) ReServiceName() string { + return m.reServiceNameField +} + +// SetReServiceName sets the re service name of this subtype +func (m *MTOServiceItemInternationalDestSIT) SetReServiceName(val string) { + m.reServiceNameField = val +} + +// RejectionReason gets the rejection reason of this subtype +func (m *MTOServiceItemInternationalDestSIT) RejectionReason() *string { + return m.rejectionReasonField +} + +// SetRejectionReason sets the rejection reason of this subtype +func (m *MTOServiceItemInternationalDestSIT) SetRejectionReason(val *string) { + m.rejectionReasonField = val +} + +// ServiceRequestDocuments gets the service request documents of this subtype +func (m *MTOServiceItemInternationalDestSIT) ServiceRequestDocuments() ServiceRequestDocuments { + return m.serviceRequestDocumentsField +} + +// SetServiceRequestDocuments sets the service request documents of this subtype +func (m *MTOServiceItemInternationalDestSIT) SetServiceRequestDocuments(val ServiceRequestDocuments) { + m.serviceRequestDocumentsField = val +} + +// Status gets the status of this subtype +func (m *MTOServiceItemInternationalDestSIT) Status() MTOServiceItemStatus { + return m.statusField +} + +// SetStatus sets the status of this subtype +func (m *MTOServiceItemInternationalDestSIT) SetStatus(val MTOServiceItemStatus) { + m.statusField = val +} + +// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure +func (m *MTOServiceItemInternationalDestSIT) UnmarshalJSON(raw []byte) error { + var data struct { + + // Date of attempted contact by the prime corresponding to `timeMilitary1`. + // Format: date + DateOfContact1 *strfmt.Date `json:"dateOfContact1,omitempty"` + + // Date of attempted contact by the prime corresponding to `timeMilitary2`. + // Format: date + DateOfContact2 *strfmt.Date `json:"dateOfContact2,omitempty"` + + // First available date that Prime can deliver SIT service item. + // Format: date + FirstAvailableDeliveryDate1 *strfmt.Date `json:"firstAvailableDeliveryDate1,omitempty"` + + // Second available date that Prime can deliver SIT service item. + // Format: date + FirstAvailableDeliveryDate2 *strfmt.Date `json:"firstAvailableDeliveryDate2,omitempty"` + + // Service code allowed for this model type. + // Required: true + // Enum: [IDFSIT IDASIT] + ReServiceCode *string `json:"reServiceCode"` + + // The reason item has been placed in SIT. + // + // Required: true + Reason *string `json:"reason"` + + // Date when the customer contacted the prime for a delivery out of SIT. + // Format: date + SitCustomerContacted *strfmt.Date `json:"sitCustomerContacted,omitempty"` + + // Departure date for SIT. This is the end date of the SIT at either origin or destination. This is optional as it can be updated using the UpdateMTOServiceItemSIT modelType at a later date. + // Format: date + SitDepartureDate *strfmt.Date `json:"sitDepartureDate,omitempty"` + + // sit destination final address + SitDestinationFinalAddress *Address `json:"sitDestinationFinalAddress,omitempty"` + + // Entry date for the SIT + // Required: true + // Format: date + SitEntryDate *strfmt.Date `json:"sitEntryDate"` + + // Date when the customer has requested delivery out of SIT. + // Format: date + SitRequestedDelivery *strfmt.Date `json:"sitRequestedDelivery,omitempty"` + + // Time of attempted contact corresponding to `dateOfContact1`, in military format. + // Example: 1400Z + // Pattern: \d{4}Z + TimeMilitary1 *string `json:"timeMilitary1,omitempty"` + + // Time of attempted contact corresponding to `dateOfContact2`, in military format. + // Example: 1400Z + // Pattern: \d{4}Z + TimeMilitary2 *string `json:"timeMilitary2,omitempty"` + } + buf := bytes.NewBuffer(raw) + dec := json.NewDecoder(buf) + dec.UseNumber() + + if err := dec.Decode(&data); err != nil { + return err + } + + var base struct { + /* Just the base type fields. Used for unmashalling polymorphic types.*/ + + ETag string `json:"eTag,omitempty"` + + ID strfmt.UUID `json:"id,omitempty"` + + LockedPriceCents *int64 `json:"lockedPriceCents,omitempty"` + + ModelType MTOServiceItemModelType `json:"modelType"` + + MoveTaskOrderID *strfmt.UUID `json:"moveTaskOrderID"` + + MtoShipmentID strfmt.UUID `json:"mtoShipmentID,omitempty"` + + ReServiceName string `json:"reServiceName,omitempty"` + + RejectionReason *string `json:"rejectionReason,omitempty"` + + ServiceRequestDocuments ServiceRequestDocuments `json:"serviceRequestDocuments,omitempty"` + + Status MTOServiceItemStatus `json:"status,omitempty"` + } + buf = bytes.NewBuffer(raw) + dec = json.NewDecoder(buf) + dec.UseNumber() + + if err := dec.Decode(&base); err != nil { + return err + } + + var result MTOServiceItemInternationalDestSIT + + result.eTagField = base.ETag + + result.idField = base.ID + + result.lockedPriceCentsField = base.LockedPriceCents + + if base.ModelType != result.ModelType() { + /* Not the type we're looking for. */ + return errors.New(422, "invalid modelType value: %q", base.ModelType) + } + result.moveTaskOrderIdField = base.MoveTaskOrderID + + result.mtoShipmentIdField = base.MtoShipmentID + + result.reServiceNameField = base.ReServiceName + + result.rejectionReasonField = base.RejectionReason + + result.serviceRequestDocumentsField = base.ServiceRequestDocuments + + result.statusField = base.Status + + result.DateOfContact1 = data.DateOfContact1 + result.DateOfContact2 = data.DateOfContact2 + result.FirstAvailableDeliveryDate1 = data.FirstAvailableDeliveryDate1 + result.FirstAvailableDeliveryDate2 = data.FirstAvailableDeliveryDate2 + result.ReServiceCode = data.ReServiceCode + result.Reason = data.Reason + result.SitCustomerContacted = data.SitCustomerContacted + result.SitDepartureDate = data.SitDepartureDate + result.SitDestinationFinalAddress = data.SitDestinationFinalAddress + result.SitEntryDate = data.SitEntryDate + result.SitRequestedDelivery = data.SitRequestedDelivery + result.TimeMilitary1 = data.TimeMilitary1 + result.TimeMilitary2 = data.TimeMilitary2 + + *m = result + + return nil +} + +// MarshalJSON marshals this object with a polymorphic type to a JSON structure +func (m MTOServiceItemInternationalDestSIT) MarshalJSON() ([]byte, error) { + var b1, b2, b3 []byte + var err error + b1, err = json.Marshal(struct { + + // Date of attempted contact by the prime corresponding to `timeMilitary1`. + // Format: date + DateOfContact1 *strfmt.Date `json:"dateOfContact1,omitempty"` + + // Date of attempted contact by the prime corresponding to `timeMilitary2`. + // Format: date + DateOfContact2 *strfmt.Date `json:"dateOfContact2,omitempty"` + + // First available date that Prime can deliver SIT service item. + // Format: date + FirstAvailableDeliveryDate1 *strfmt.Date `json:"firstAvailableDeliveryDate1,omitempty"` + + // Second available date that Prime can deliver SIT service item. + // Format: date + FirstAvailableDeliveryDate2 *strfmt.Date `json:"firstAvailableDeliveryDate2,omitempty"` + + // Service code allowed for this model type. + // Required: true + // Enum: [IDFSIT IDASIT] + ReServiceCode *string `json:"reServiceCode"` + + // The reason item has been placed in SIT. + // + // Required: true + Reason *string `json:"reason"` + + // Date when the customer contacted the prime for a delivery out of SIT. + // Format: date + SitCustomerContacted *strfmt.Date `json:"sitCustomerContacted,omitempty"` + + // Departure date for SIT. This is the end date of the SIT at either origin or destination. This is optional as it can be updated using the UpdateMTOServiceItemSIT modelType at a later date. + // Format: date + SitDepartureDate *strfmt.Date `json:"sitDepartureDate,omitempty"` + + // sit destination final address + SitDestinationFinalAddress *Address `json:"sitDestinationFinalAddress,omitempty"` + + // Entry date for the SIT + // Required: true + // Format: date + SitEntryDate *strfmt.Date `json:"sitEntryDate"` + + // Date when the customer has requested delivery out of SIT. + // Format: date + SitRequestedDelivery *strfmt.Date `json:"sitRequestedDelivery,omitempty"` + + // Time of attempted contact corresponding to `dateOfContact1`, in military format. + // Example: 1400Z + // Pattern: \d{4}Z + TimeMilitary1 *string `json:"timeMilitary1,omitempty"` + + // Time of attempted contact corresponding to `dateOfContact2`, in military format. + // Example: 1400Z + // Pattern: \d{4}Z + TimeMilitary2 *string `json:"timeMilitary2,omitempty"` + }{ + + DateOfContact1: m.DateOfContact1, + + DateOfContact2: m.DateOfContact2, + + FirstAvailableDeliveryDate1: m.FirstAvailableDeliveryDate1, + + FirstAvailableDeliveryDate2: m.FirstAvailableDeliveryDate2, + + ReServiceCode: m.ReServiceCode, + + Reason: m.Reason, + + SitCustomerContacted: m.SitCustomerContacted, + + SitDepartureDate: m.SitDepartureDate, + + SitDestinationFinalAddress: m.SitDestinationFinalAddress, + + SitEntryDate: m.SitEntryDate, + + SitRequestedDelivery: m.SitRequestedDelivery, + + TimeMilitary1: m.TimeMilitary1, + + TimeMilitary2: m.TimeMilitary2, + }) + if err != nil { + return nil, err + } + b2, err = json.Marshal(struct { + ETag string `json:"eTag,omitempty"` + + ID strfmt.UUID `json:"id,omitempty"` + + LockedPriceCents *int64 `json:"lockedPriceCents,omitempty"` + + ModelType MTOServiceItemModelType `json:"modelType"` + + MoveTaskOrderID *strfmt.UUID `json:"moveTaskOrderID"` + + MtoShipmentID strfmt.UUID `json:"mtoShipmentID,omitempty"` + + ReServiceName string `json:"reServiceName,omitempty"` + + RejectionReason *string `json:"rejectionReason,omitempty"` + + ServiceRequestDocuments ServiceRequestDocuments `json:"serviceRequestDocuments,omitempty"` + + Status MTOServiceItemStatus `json:"status,omitempty"` + }{ + + ETag: m.ETag(), + + ID: m.ID(), + + LockedPriceCents: m.LockedPriceCents(), + + ModelType: m.ModelType(), + + MoveTaskOrderID: m.MoveTaskOrderID(), + + MtoShipmentID: m.MtoShipmentID(), + + ReServiceName: m.ReServiceName(), + + RejectionReason: m.RejectionReason(), + + ServiceRequestDocuments: m.ServiceRequestDocuments(), + + Status: m.Status(), + }) + if err != nil { + return nil, err + } + + return swag.ConcatJSON(b1, b2, b3), nil +} + +// Validate validates this m t o service item international dest s i t +func (m *MTOServiceItemInternationalDestSIT) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateMoveTaskOrderID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateMtoShipmentID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateServiceRequestDocuments(formats); err != nil { + res = append(res, err) + } + + if err := m.validateStatus(formats); err != nil { + res = append(res, err) + } + + if err := m.validateDateOfContact1(formats); err != nil { + res = append(res, err) + } + + if err := m.validateDateOfContact2(formats); err != nil { + res = append(res, err) + } + + if err := m.validateFirstAvailableDeliveryDate1(formats); err != nil { + res = append(res, err) + } + + if err := m.validateFirstAvailableDeliveryDate2(formats); err != nil { + res = append(res, err) + } + + if err := m.validateReServiceCode(formats); err != nil { + res = append(res, err) + } + + if err := m.validateReason(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSitCustomerContacted(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSitDepartureDate(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSitDestinationFinalAddress(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSitEntryDate(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSitRequestedDelivery(formats); err != nil { + res = append(res, err) + } + + if err := m.validateTimeMilitary1(formats); err != nil { + res = append(res, err) + } + + if err := m.validateTimeMilitary2(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateID(formats strfmt.Registry) error { + + if swag.IsZero(m.ID()) { // not required + return nil + } + + if err := validate.FormatOf("id", "body", "uuid", m.ID().String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateMoveTaskOrderID(formats strfmt.Registry) error { + + if err := validate.Required("moveTaskOrderID", "body", m.MoveTaskOrderID()); err != nil { + return err + } + + if err := validate.FormatOf("moveTaskOrderID", "body", "uuid", m.MoveTaskOrderID().String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateMtoShipmentID(formats strfmt.Registry) error { + + if swag.IsZero(m.MtoShipmentID()) { // not required + return nil + } + + if err := validate.FormatOf("mtoShipmentID", "body", "uuid", m.MtoShipmentID().String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateServiceRequestDocuments(formats strfmt.Registry) error { + + if swag.IsZero(m.ServiceRequestDocuments()) { // not required + return nil + } + + if err := m.ServiceRequestDocuments().Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("serviceRequestDocuments") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("serviceRequestDocuments") + } + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateStatus(formats strfmt.Registry) error { + + if swag.IsZero(m.Status()) { // not required + return nil + } + + if err := m.Status().Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("status") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("status") + } + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateDateOfContact1(formats strfmt.Registry) error { + + if swag.IsZero(m.DateOfContact1) { // not required + return nil + } + + if err := validate.FormatOf("dateOfContact1", "body", "date", m.DateOfContact1.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateDateOfContact2(formats strfmt.Registry) error { + + if swag.IsZero(m.DateOfContact2) { // not required + return nil + } + + if err := validate.FormatOf("dateOfContact2", "body", "date", m.DateOfContact2.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateFirstAvailableDeliveryDate1(formats strfmt.Registry) error { + + if swag.IsZero(m.FirstAvailableDeliveryDate1) { // not required + return nil + } + + if err := validate.FormatOf("firstAvailableDeliveryDate1", "body", "date", m.FirstAvailableDeliveryDate1.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateFirstAvailableDeliveryDate2(formats strfmt.Registry) error { + + if swag.IsZero(m.FirstAvailableDeliveryDate2) { // not required + return nil + } + + if err := validate.FormatOf("firstAvailableDeliveryDate2", "body", "date", m.FirstAvailableDeliveryDate2.String(), formats); err != nil { + return err + } + + return nil +} + +var mTOServiceItemInternationalDestSITTypeReServiceCodePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["IDFSIT","IDASIT"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + mTOServiceItemInternationalDestSITTypeReServiceCodePropEnum = append(mTOServiceItemInternationalDestSITTypeReServiceCodePropEnum, v) + } +} + +// property enum +func (m *MTOServiceItemInternationalDestSIT) validateReServiceCodeEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, mTOServiceItemInternationalDestSITTypeReServiceCodePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateReServiceCode(formats strfmt.Registry) error { + + if err := validate.Required("reServiceCode", "body", m.ReServiceCode); err != nil { + return err + } + + // value enum + if err := m.validateReServiceCodeEnum("reServiceCode", "body", *m.ReServiceCode); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateReason(formats strfmt.Registry) error { + + if err := validate.Required("reason", "body", m.Reason); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateSitCustomerContacted(formats strfmt.Registry) error { + + if swag.IsZero(m.SitCustomerContacted) { // not required + return nil + } + + if err := validate.FormatOf("sitCustomerContacted", "body", "date", m.SitCustomerContacted.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateSitDepartureDate(formats strfmt.Registry) error { + + if swag.IsZero(m.SitDepartureDate) { // not required + return nil + } + + if err := validate.FormatOf("sitDepartureDate", "body", "date", m.SitDepartureDate.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateSitDestinationFinalAddress(formats strfmt.Registry) error { + + if swag.IsZero(m.SitDestinationFinalAddress) { // not required + return nil + } + + if m.SitDestinationFinalAddress != nil { + if err := m.SitDestinationFinalAddress.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("sitDestinationFinalAddress") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("sitDestinationFinalAddress") + } + return err + } + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateSitEntryDate(formats strfmt.Registry) error { + + if err := validate.Required("sitEntryDate", "body", m.SitEntryDate); err != nil { + return err + } + + if err := validate.FormatOf("sitEntryDate", "body", "date", m.SitEntryDate.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateSitRequestedDelivery(formats strfmt.Registry) error { + + if swag.IsZero(m.SitRequestedDelivery) { // not required + return nil + } + + if err := validate.FormatOf("sitRequestedDelivery", "body", "date", m.SitRequestedDelivery.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateTimeMilitary1(formats strfmt.Registry) error { + + if swag.IsZero(m.TimeMilitary1) { // not required + return nil + } + + if err := validate.Pattern("timeMilitary1", "body", *m.TimeMilitary1, `\d{4}Z`); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) validateTimeMilitary2(formats strfmt.Registry) error { + + if swag.IsZero(m.TimeMilitary2) { // not required + return nil + } + + if err := validate.Pattern("timeMilitary2", "body", *m.TimeMilitary2, `\d{4}Z`); err != nil { + return err + } + + return nil +} + +// ContextValidate validate this m t o service item international dest s i t based on the context it is used +func (m *MTOServiceItemInternationalDestSIT) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateETag(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateID(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateReServiceName(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateRejectionReason(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateServiceRequestDocuments(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateStatus(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateSitDestinationFinalAddress(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) contextValidateETag(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "eTag", "body", string(m.ETag())); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) contextValidateID(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "id", "body", strfmt.UUID(m.ID())); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) contextValidateModelType(ctx context.Context, formats strfmt.Registry) error { + + if err := m.ModelType().ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("modelType") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("modelType") + } + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) contextValidateReServiceName(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "reServiceName", "body", string(m.ReServiceName())); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) contextValidateRejectionReason(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "rejectionReason", "body", m.RejectionReason()); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) contextValidateServiceRequestDocuments(ctx context.Context, formats strfmt.Registry) error { + + if err := m.ServiceRequestDocuments().ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("serviceRequestDocuments") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("serviceRequestDocuments") + } + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) contextValidateStatus(ctx context.Context, formats strfmt.Registry) error { + + if swag.IsZero(m.Status()) { // not required + return nil + } + + if err := m.Status().ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("status") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("status") + } + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalDestSIT) contextValidateSitDestinationFinalAddress(ctx context.Context, formats strfmt.Registry) error { + + if m.SitDestinationFinalAddress != nil { + + if swag.IsZero(m.SitDestinationFinalAddress) { // not required + return nil + } + + if err := m.SitDestinationFinalAddress.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("sitDestinationFinalAddress") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("sitDestinationFinalAddress") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *MTOServiceItemInternationalDestSIT) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *MTOServiceItemInternationalDestSIT) UnmarshalBinary(b []byte) error { + var res MTOServiceItemInternationalDestSIT + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/gen/primemessages/m_t_o_service_item_international_origin_s_i_t.go b/pkg/gen/primemessages/m_t_o_service_item_international_origin_s_i_t.go new file mode 100644 index 00000000000..96392736360 --- /dev/null +++ b/pkg/gen/primemessages/m_t_o_service_item_international_origin_s_i_t.go @@ -0,0 +1,900 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package primemessages + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "bytes" + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// MTOServiceItemInternationalOriginSIT Describes a international origin SIT service item. Subtype of a MTOServiceItem. +// +// swagger:model MTOServiceItemInternationalOriginSIT +type MTOServiceItemInternationalOriginSIT struct { + eTagField string + + idField strfmt.UUID + + lockedPriceCentsField *int64 + + moveTaskOrderIdField *strfmt.UUID + + mtoShipmentIdField strfmt.UUID + + reServiceNameField string + + rejectionReasonField *string + + serviceRequestDocumentsField ServiceRequestDocuments + + statusField MTOServiceItemStatus + + // Service code allowed for this model type. + // Required: true + // Enum: [IOFSIT IOASIT] + ReServiceCode *string `json:"reServiceCode"` + + // Explanation of why Prime is picking up SIT item. + // Example: Storage items need to be picked up + // Required: true + Reason *string `json:"reason"` + + // request approvals requested status + RequestApprovalsRequestedStatus bool `json:"requestApprovalsRequestedStatus,omitempty"` + + // Date when the customer contacted the prime for a delivery out of SIT. + // Format: date + SitCustomerContacted *strfmt.Date `json:"sitCustomerContacted,omitempty"` + + // Departure date for SIT. This is the end date of the SIT at either origin or destination. This is optional as it can be updated using the UpdateMTOServiceItemSIT modelType at a later date. + // Format: date + SitDepartureDate *strfmt.Date `json:"sitDepartureDate,omitempty"` + + // Entry date for the SIT + // Required: true + // Format: date + SitEntryDate *strfmt.Date `json:"sitEntryDate"` + + // sit h h g actual origin + SitHHGActualOrigin *Address `json:"sitHHGActualOrigin,omitempty"` + + // sit h h g original origin + SitHHGOriginalOrigin *Address `json:"sitHHGOriginalOrigin,omitempty"` + + // sit postal code + // Example: 90210 + // Required: true + // Pattern: ^(\d{5}([\-]\d{4})?)$ + SitPostalCode *string `json:"sitPostalCode"` + + // Date when the customer has requested delivery out of SIT. + // Format: date + SitRequestedDelivery *strfmt.Date `json:"sitRequestedDelivery,omitempty"` +} + +// ETag gets the e tag of this subtype +func (m *MTOServiceItemInternationalOriginSIT) ETag() string { + return m.eTagField +} + +// SetETag sets the e tag of this subtype +func (m *MTOServiceItemInternationalOriginSIT) SetETag(val string) { + m.eTagField = val +} + +// ID gets the id of this subtype +func (m *MTOServiceItemInternationalOriginSIT) ID() strfmt.UUID { + return m.idField +} + +// SetID sets the id of this subtype +func (m *MTOServiceItemInternationalOriginSIT) SetID(val strfmt.UUID) { + m.idField = val +} + +// LockedPriceCents gets the locked price cents of this subtype +func (m *MTOServiceItemInternationalOriginSIT) LockedPriceCents() *int64 { + return m.lockedPriceCentsField +} + +// SetLockedPriceCents sets the locked price cents of this subtype +func (m *MTOServiceItemInternationalOriginSIT) SetLockedPriceCents(val *int64) { + m.lockedPriceCentsField = val +} + +// ModelType gets the model type of this subtype +func (m *MTOServiceItemInternationalOriginSIT) ModelType() MTOServiceItemModelType { + return "MTOServiceItemInternationalOriginSIT" +} + +// SetModelType sets the model type of this subtype +func (m *MTOServiceItemInternationalOriginSIT) SetModelType(val MTOServiceItemModelType) { +} + +// MoveTaskOrderID gets the move task order ID of this subtype +func (m *MTOServiceItemInternationalOriginSIT) MoveTaskOrderID() *strfmt.UUID { + return m.moveTaskOrderIdField +} + +// SetMoveTaskOrderID sets the move task order ID of this subtype +func (m *MTOServiceItemInternationalOriginSIT) SetMoveTaskOrderID(val *strfmt.UUID) { + m.moveTaskOrderIdField = val +} + +// MtoShipmentID gets the mto shipment ID of this subtype +func (m *MTOServiceItemInternationalOriginSIT) MtoShipmentID() strfmt.UUID { + return m.mtoShipmentIdField +} + +// SetMtoShipmentID sets the mto shipment ID of this subtype +func (m *MTOServiceItemInternationalOriginSIT) SetMtoShipmentID(val strfmt.UUID) { + m.mtoShipmentIdField = val +} + +// ReServiceName gets the re service name of this subtype +func (m *MTOServiceItemInternationalOriginSIT) ReServiceName() string { + return m.reServiceNameField +} + +// SetReServiceName sets the re service name of this subtype +func (m *MTOServiceItemInternationalOriginSIT) SetReServiceName(val string) { + m.reServiceNameField = val +} + +// RejectionReason gets the rejection reason of this subtype +func (m *MTOServiceItemInternationalOriginSIT) RejectionReason() *string { + return m.rejectionReasonField +} + +// SetRejectionReason sets the rejection reason of this subtype +func (m *MTOServiceItemInternationalOriginSIT) SetRejectionReason(val *string) { + m.rejectionReasonField = val +} + +// ServiceRequestDocuments gets the service request documents of this subtype +func (m *MTOServiceItemInternationalOriginSIT) ServiceRequestDocuments() ServiceRequestDocuments { + return m.serviceRequestDocumentsField +} + +// SetServiceRequestDocuments sets the service request documents of this subtype +func (m *MTOServiceItemInternationalOriginSIT) SetServiceRequestDocuments(val ServiceRequestDocuments) { + m.serviceRequestDocumentsField = val +} + +// Status gets the status of this subtype +func (m *MTOServiceItemInternationalOriginSIT) Status() MTOServiceItemStatus { + return m.statusField +} + +// SetStatus sets the status of this subtype +func (m *MTOServiceItemInternationalOriginSIT) SetStatus(val MTOServiceItemStatus) { + m.statusField = val +} + +// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure +func (m *MTOServiceItemInternationalOriginSIT) UnmarshalJSON(raw []byte) error { + var data struct { + + // Service code allowed for this model type. + // Required: true + // Enum: [IOFSIT IOASIT] + ReServiceCode *string `json:"reServiceCode"` + + // Explanation of why Prime is picking up SIT item. + // Example: Storage items need to be picked up + // Required: true + Reason *string `json:"reason"` + + // request approvals requested status + RequestApprovalsRequestedStatus bool `json:"requestApprovalsRequestedStatus,omitempty"` + + // Date when the customer contacted the prime for a delivery out of SIT. + // Format: date + SitCustomerContacted *strfmt.Date `json:"sitCustomerContacted,omitempty"` + + // Departure date for SIT. This is the end date of the SIT at either origin or destination. This is optional as it can be updated using the UpdateMTOServiceItemSIT modelType at a later date. + // Format: date + SitDepartureDate *strfmt.Date `json:"sitDepartureDate,omitempty"` + + // Entry date for the SIT + // Required: true + // Format: date + SitEntryDate *strfmt.Date `json:"sitEntryDate"` + + // sit h h g actual origin + SitHHGActualOrigin *Address `json:"sitHHGActualOrigin,omitempty"` + + // sit h h g original origin + SitHHGOriginalOrigin *Address `json:"sitHHGOriginalOrigin,omitempty"` + + // sit postal code + // Example: 90210 + // Required: true + // Pattern: ^(\d{5}([\-]\d{4})?)$ + SitPostalCode *string `json:"sitPostalCode"` + + // Date when the customer has requested delivery out of SIT. + // Format: date + SitRequestedDelivery *strfmt.Date `json:"sitRequestedDelivery,omitempty"` + } + buf := bytes.NewBuffer(raw) + dec := json.NewDecoder(buf) + dec.UseNumber() + + if err := dec.Decode(&data); err != nil { + return err + } + + var base struct { + /* Just the base type fields. Used for unmashalling polymorphic types.*/ + + ETag string `json:"eTag,omitempty"` + + ID strfmt.UUID `json:"id,omitempty"` + + LockedPriceCents *int64 `json:"lockedPriceCents,omitempty"` + + ModelType MTOServiceItemModelType `json:"modelType"` + + MoveTaskOrderID *strfmt.UUID `json:"moveTaskOrderID"` + + MtoShipmentID strfmt.UUID `json:"mtoShipmentID,omitempty"` + + ReServiceName string `json:"reServiceName,omitempty"` + + RejectionReason *string `json:"rejectionReason,omitempty"` + + ServiceRequestDocuments ServiceRequestDocuments `json:"serviceRequestDocuments,omitempty"` + + Status MTOServiceItemStatus `json:"status,omitempty"` + } + buf = bytes.NewBuffer(raw) + dec = json.NewDecoder(buf) + dec.UseNumber() + + if err := dec.Decode(&base); err != nil { + return err + } + + var result MTOServiceItemInternationalOriginSIT + + result.eTagField = base.ETag + + result.idField = base.ID + + result.lockedPriceCentsField = base.LockedPriceCents + + if base.ModelType != result.ModelType() { + /* Not the type we're looking for. */ + return errors.New(422, "invalid modelType value: %q", base.ModelType) + } + result.moveTaskOrderIdField = base.MoveTaskOrderID + + result.mtoShipmentIdField = base.MtoShipmentID + + result.reServiceNameField = base.ReServiceName + + result.rejectionReasonField = base.RejectionReason + + result.serviceRequestDocumentsField = base.ServiceRequestDocuments + + result.statusField = base.Status + + result.ReServiceCode = data.ReServiceCode + result.Reason = data.Reason + result.RequestApprovalsRequestedStatus = data.RequestApprovalsRequestedStatus + result.SitCustomerContacted = data.SitCustomerContacted + result.SitDepartureDate = data.SitDepartureDate + result.SitEntryDate = data.SitEntryDate + result.SitHHGActualOrigin = data.SitHHGActualOrigin + result.SitHHGOriginalOrigin = data.SitHHGOriginalOrigin + result.SitPostalCode = data.SitPostalCode + result.SitRequestedDelivery = data.SitRequestedDelivery + + *m = result + + return nil +} + +// MarshalJSON marshals this object with a polymorphic type to a JSON structure +func (m MTOServiceItemInternationalOriginSIT) MarshalJSON() ([]byte, error) { + var b1, b2, b3 []byte + var err error + b1, err = json.Marshal(struct { + + // Service code allowed for this model type. + // Required: true + // Enum: [IOFSIT IOASIT] + ReServiceCode *string `json:"reServiceCode"` + + // Explanation of why Prime is picking up SIT item. + // Example: Storage items need to be picked up + // Required: true + Reason *string `json:"reason"` + + // request approvals requested status + RequestApprovalsRequestedStatus bool `json:"requestApprovalsRequestedStatus,omitempty"` + + // Date when the customer contacted the prime for a delivery out of SIT. + // Format: date + SitCustomerContacted *strfmt.Date `json:"sitCustomerContacted,omitempty"` + + // Departure date for SIT. This is the end date of the SIT at either origin or destination. This is optional as it can be updated using the UpdateMTOServiceItemSIT modelType at a later date. + // Format: date + SitDepartureDate *strfmt.Date `json:"sitDepartureDate,omitempty"` + + // Entry date for the SIT + // Required: true + // Format: date + SitEntryDate *strfmt.Date `json:"sitEntryDate"` + + // sit h h g actual origin + SitHHGActualOrigin *Address `json:"sitHHGActualOrigin,omitempty"` + + // sit h h g original origin + SitHHGOriginalOrigin *Address `json:"sitHHGOriginalOrigin,omitempty"` + + // sit postal code + // Example: 90210 + // Required: true + // Pattern: ^(\d{5}([\-]\d{4})?)$ + SitPostalCode *string `json:"sitPostalCode"` + + // Date when the customer has requested delivery out of SIT. + // Format: date + SitRequestedDelivery *strfmt.Date `json:"sitRequestedDelivery,omitempty"` + }{ + + ReServiceCode: m.ReServiceCode, + + Reason: m.Reason, + + RequestApprovalsRequestedStatus: m.RequestApprovalsRequestedStatus, + + SitCustomerContacted: m.SitCustomerContacted, + + SitDepartureDate: m.SitDepartureDate, + + SitEntryDate: m.SitEntryDate, + + SitHHGActualOrigin: m.SitHHGActualOrigin, + + SitHHGOriginalOrigin: m.SitHHGOriginalOrigin, + + SitPostalCode: m.SitPostalCode, + + SitRequestedDelivery: m.SitRequestedDelivery, + }) + if err != nil { + return nil, err + } + b2, err = json.Marshal(struct { + ETag string `json:"eTag,omitempty"` + + ID strfmt.UUID `json:"id,omitempty"` + + LockedPriceCents *int64 `json:"lockedPriceCents,omitempty"` + + ModelType MTOServiceItemModelType `json:"modelType"` + + MoveTaskOrderID *strfmt.UUID `json:"moveTaskOrderID"` + + MtoShipmentID strfmt.UUID `json:"mtoShipmentID,omitempty"` + + ReServiceName string `json:"reServiceName,omitempty"` + + RejectionReason *string `json:"rejectionReason,omitempty"` + + ServiceRequestDocuments ServiceRequestDocuments `json:"serviceRequestDocuments,omitempty"` + + Status MTOServiceItemStatus `json:"status,omitempty"` + }{ + + ETag: m.ETag(), + + ID: m.ID(), + + LockedPriceCents: m.LockedPriceCents(), + + ModelType: m.ModelType(), + + MoveTaskOrderID: m.MoveTaskOrderID(), + + MtoShipmentID: m.MtoShipmentID(), + + ReServiceName: m.ReServiceName(), + + RejectionReason: m.RejectionReason(), + + ServiceRequestDocuments: m.ServiceRequestDocuments(), + + Status: m.Status(), + }) + if err != nil { + return nil, err + } + + return swag.ConcatJSON(b1, b2, b3), nil +} + +// Validate validates this m t o service item international origin s i t +func (m *MTOServiceItemInternationalOriginSIT) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateMoveTaskOrderID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateMtoShipmentID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateServiceRequestDocuments(formats); err != nil { + res = append(res, err) + } + + if err := m.validateStatus(formats); err != nil { + res = append(res, err) + } + + if err := m.validateReServiceCode(formats); err != nil { + res = append(res, err) + } + + if err := m.validateReason(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSitCustomerContacted(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSitDepartureDate(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSitEntryDate(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSitHHGActualOrigin(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSitHHGOriginalOrigin(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSitPostalCode(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSitRequestedDelivery(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateID(formats strfmt.Registry) error { + + if swag.IsZero(m.ID()) { // not required + return nil + } + + if err := validate.FormatOf("id", "body", "uuid", m.ID().String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateMoveTaskOrderID(formats strfmt.Registry) error { + + if err := validate.Required("moveTaskOrderID", "body", m.MoveTaskOrderID()); err != nil { + return err + } + + if err := validate.FormatOf("moveTaskOrderID", "body", "uuid", m.MoveTaskOrderID().String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateMtoShipmentID(formats strfmt.Registry) error { + + if swag.IsZero(m.MtoShipmentID()) { // not required + return nil + } + + if err := validate.FormatOf("mtoShipmentID", "body", "uuid", m.MtoShipmentID().String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateServiceRequestDocuments(formats strfmt.Registry) error { + + if swag.IsZero(m.ServiceRequestDocuments()) { // not required + return nil + } + + if err := m.ServiceRequestDocuments().Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("serviceRequestDocuments") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("serviceRequestDocuments") + } + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateStatus(formats strfmt.Registry) error { + + if swag.IsZero(m.Status()) { // not required + return nil + } + + if err := m.Status().Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("status") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("status") + } + return err + } + + return nil +} + +var mTOServiceItemInternationalOriginSITTypeReServiceCodePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["IOFSIT","IOASIT"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + mTOServiceItemInternationalOriginSITTypeReServiceCodePropEnum = append(mTOServiceItemInternationalOriginSITTypeReServiceCodePropEnum, v) + } +} + +// property enum +func (m *MTOServiceItemInternationalOriginSIT) validateReServiceCodeEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, mTOServiceItemInternationalOriginSITTypeReServiceCodePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateReServiceCode(formats strfmt.Registry) error { + + if err := validate.Required("reServiceCode", "body", m.ReServiceCode); err != nil { + return err + } + + // value enum + if err := m.validateReServiceCodeEnum("reServiceCode", "body", *m.ReServiceCode); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateReason(formats strfmt.Registry) error { + + if err := validate.Required("reason", "body", m.Reason); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateSitCustomerContacted(formats strfmt.Registry) error { + + if swag.IsZero(m.SitCustomerContacted) { // not required + return nil + } + + if err := validate.FormatOf("sitCustomerContacted", "body", "date", m.SitCustomerContacted.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateSitDepartureDate(formats strfmt.Registry) error { + + if swag.IsZero(m.SitDepartureDate) { // not required + return nil + } + + if err := validate.FormatOf("sitDepartureDate", "body", "date", m.SitDepartureDate.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateSitEntryDate(formats strfmt.Registry) error { + + if err := validate.Required("sitEntryDate", "body", m.SitEntryDate); err != nil { + return err + } + + if err := validate.FormatOf("sitEntryDate", "body", "date", m.SitEntryDate.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateSitHHGActualOrigin(formats strfmt.Registry) error { + + if swag.IsZero(m.SitHHGActualOrigin) { // not required + return nil + } + + if m.SitHHGActualOrigin != nil { + if err := m.SitHHGActualOrigin.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("sitHHGActualOrigin") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("sitHHGActualOrigin") + } + return err + } + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateSitHHGOriginalOrigin(formats strfmt.Registry) error { + + if swag.IsZero(m.SitHHGOriginalOrigin) { // not required + return nil + } + + if m.SitHHGOriginalOrigin != nil { + if err := m.SitHHGOriginalOrigin.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("sitHHGOriginalOrigin") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("sitHHGOriginalOrigin") + } + return err + } + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateSitPostalCode(formats strfmt.Registry) error { + + if err := validate.Required("sitPostalCode", "body", m.SitPostalCode); err != nil { + return err + } + + if err := validate.Pattern("sitPostalCode", "body", *m.SitPostalCode, `^(\d{5}([\-]\d{4})?)$`); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) validateSitRequestedDelivery(formats strfmt.Registry) error { + + if swag.IsZero(m.SitRequestedDelivery) { // not required + return nil + } + + if err := validate.FormatOf("sitRequestedDelivery", "body", "date", m.SitRequestedDelivery.String(), formats); err != nil { + return err + } + + return nil +} + +// ContextValidate validate this m t o service item international origin s i t based on the context it is used +func (m *MTOServiceItemInternationalOriginSIT) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateETag(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateID(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateReServiceName(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateRejectionReason(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateServiceRequestDocuments(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateStatus(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateSitHHGActualOrigin(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateSitHHGOriginalOrigin(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) contextValidateETag(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "eTag", "body", string(m.ETag())); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) contextValidateID(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "id", "body", strfmt.UUID(m.ID())); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) contextValidateModelType(ctx context.Context, formats strfmt.Registry) error { + + if err := m.ModelType().ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("modelType") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("modelType") + } + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) contextValidateReServiceName(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "reServiceName", "body", string(m.ReServiceName())); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) contextValidateRejectionReason(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "rejectionReason", "body", m.RejectionReason()); err != nil { + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) contextValidateServiceRequestDocuments(ctx context.Context, formats strfmt.Registry) error { + + if err := m.ServiceRequestDocuments().ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("serviceRequestDocuments") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("serviceRequestDocuments") + } + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) contextValidateStatus(ctx context.Context, formats strfmt.Registry) error { + + if swag.IsZero(m.Status()) { // not required + return nil + } + + if err := m.Status().ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("status") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("status") + } + return err + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) contextValidateSitHHGActualOrigin(ctx context.Context, formats strfmt.Registry) error { + + if m.SitHHGActualOrigin != nil { + + if swag.IsZero(m.SitHHGActualOrigin) { // not required + return nil + } + + if err := m.SitHHGActualOrigin.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("sitHHGActualOrigin") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("sitHHGActualOrigin") + } + return err + } + } + + return nil +} + +func (m *MTOServiceItemInternationalOriginSIT) contextValidateSitHHGOriginalOrigin(ctx context.Context, formats strfmt.Registry) error { + + if m.SitHHGOriginalOrigin != nil { + + if swag.IsZero(m.SitHHGOriginalOrigin) { // not required + return nil + } + + if err := m.SitHHGOriginalOrigin.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("sitHHGOriginalOrigin") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("sitHHGOriginalOrigin") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *MTOServiceItemInternationalOriginSIT) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *MTOServiceItemInternationalOriginSIT) UnmarshalBinary(b []byte) error { + var res MTOServiceItemInternationalOriginSIT + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/gen/primemessages/m_t_o_service_item_model_type.go b/pkg/gen/primemessages/m_t_o_service_item_model_type.go index ffdea3d8c15..97a7fb5aa78 100644 --- a/pkg/gen/primemessages/m_t_o_service_item_model_type.go +++ b/pkg/gen/primemessages/m_t_o_service_item_model_type.go @@ -19,6 +19,8 @@ import ( // Using this list, choose the correct modelType in the dropdown, corresponding to the service item type. // - DOFSIT, DOASIT - MTOServiceItemOriginSIT // - DDFSIT, DDASIT - MTOServiceItemDestSIT +// - IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT +// - IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT // - DOSHUT, DDSHUT - MTOServiceItemShuttle // - DCRT, DUCRT - MTOServiceItemDomesticCrating // - ICRT, IUCRT - MTOServiceItemInternationalCrating @@ -49,6 +51,12 @@ const ( // MTOServiceItemModelTypeMTOServiceItemDestSIT captures enum value "MTOServiceItemDestSIT" MTOServiceItemModelTypeMTOServiceItemDestSIT MTOServiceItemModelType = "MTOServiceItemDestSIT" + // MTOServiceItemModelTypeMTOServiceItemInternationalOriginSIT captures enum value "MTOServiceItemInternationalOriginSIT" + MTOServiceItemModelTypeMTOServiceItemInternationalOriginSIT MTOServiceItemModelType = "MTOServiceItemInternationalOriginSIT" + + // MTOServiceItemModelTypeMTOServiceItemInternationalDestSIT captures enum value "MTOServiceItemInternationalDestSIT" + MTOServiceItemModelTypeMTOServiceItemInternationalDestSIT MTOServiceItemModelType = "MTOServiceItemInternationalDestSIT" + // MTOServiceItemModelTypeMTOServiceItemShuttle captures enum value "MTOServiceItemShuttle" MTOServiceItemModelTypeMTOServiceItemShuttle MTOServiceItemModelType = "MTOServiceItemShuttle" @@ -67,7 +75,7 @@ var mTOServiceItemModelTypeEnum []interface{} func init() { var res []MTOServiceItemModelType - if err := json.Unmarshal([]byte(`["MTOServiceItemBasic","MTOServiceItemOriginSIT","MTOServiceItemDestSIT","MTOServiceItemShuttle","MTOServiceItemDomesticCrating","MTOServiceItemInternationalCrating","MTOSerivceItemInternationalFuelSurcharge"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["MTOServiceItemBasic","MTOServiceItemOriginSIT","MTOServiceItemDestSIT","MTOServiceItemInternationalOriginSIT","MTOServiceItemInternationalDestSIT","MTOServiceItemShuttle","MTOServiceItemDomesticCrating","MTOServiceItemInternationalCrating","MTOSerivceItemInternationalFuelSurcharge"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/primemessages/update_m_t_o_service_item_model_type.go b/pkg/gen/primemessages/update_m_t_o_service_item_model_type.go index 40e86f82729..0d32c178283 100644 --- a/pkg/gen/primemessages/update_m_t_o_service_item_model_type.go +++ b/pkg/gen/primemessages/update_m_t_o_service_item_model_type.go @@ -23,6 +23,14 @@ import ( // - DOFSIT - UpdateMTOServiceItemSIT // - DOSFSC - UpdateMTOServiceItemSIT // - DDSFSC - UpdateMTOServiceItemSIT +// - IDDSIT - UpdateMTOServiceItemSIT +// - IDFSIT - UpdateMTOServiceItemSIT +// - IDASIT - UpdateMTOServiceItemSIT +// - IOPSIT - UpdateMTOServiceItemSIT +// - IOASIT - UpdateMTOServiceItemSIT +// - IOFSIT - UpdateMTOServiceItemSIT +// - IOSFSC - UpdateMTOServiceItemSIT +// - IDSFSC - UpdateMTOServiceItemSIT // - DDSHUT - UpdateMTOServiceItemShuttle // - DOSHUT - UpdateMTOServiceItemShuttle // - PODFSC - UpdateMTOServiceItemInternationalPortFSC diff --git a/pkg/gen/primemessages/update_m_t_o_service_item_s_i_t.go b/pkg/gen/primemessages/update_m_t_o_service_item_s_i_t.go index 34fed36c1aa..5c76260ea6a 100644 --- a/pkg/gen/primemessages/update_m_t_o_service_item_s_i_t.go +++ b/pkg/gen/primemessages/update_m_t_o_service_item_s_i_t.go @@ -39,7 +39,7 @@ type UpdateMTOServiceItemSIT struct { FirstAvailableDeliveryDate2 *strfmt.Date `json:"firstAvailableDeliveryDate2,omitempty"` // Service code allowed for this model type. - // Enum: [DDDSIT DDASIT DDFSIT DDSFSC DOPSIT DOASIT DOFSIT DOSFSC] + // Enum: [DDDSIT DDASIT DDFSIT DDSFSC DOPSIT DOASIT DOFSIT DOSFSC IDDSIT IDASIT IDFSIT IDSFSC IOPSIT IOASIT IOFSIT IOSFSC] ReServiceCode string `json:"reServiceCode,omitempty"` // Indicates if "Approvals Requested" status is being requested. @@ -123,7 +123,7 @@ func (m *UpdateMTOServiceItemSIT) UnmarshalJSON(raw []byte) error { FirstAvailableDeliveryDate2 *strfmt.Date `json:"firstAvailableDeliveryDate2,omitempty"` // Service code allowed for this model type. - // Enum: [DDDSIT DDASIT DDFSIT DDSFSC DOPSIT DOASIT DOFSIT DOSFSC] + // Enum: [DDDSIT DDASIT DDFSIT DDSFSC DOPSIT DOASIT DOFSIT DOSFSC IDDSIT IDASIT IDFSIT IDSFSC IOPSIT IOASIT IOFSIT IOSFSC] ReServiceCode string `json:"reServiceCode,omitempty"` // Indicates if "Approvals Requested" status is being requested. @@ -242,7 +242,7 @@ func (m UpdateMTOServiceItemSIT) MarshalJSON() ([]byte, error) { FirstAvailableDeliveryDate2 *strfmt.Date `json:"firstAvailableDeliveryDate2,omitempty"` // Service code allowed for this model type. - // Enum: [DDDSIT DDASIT DDFSIT DDSFSC DOPSIT DOASIT DOFSIT DOSFSC] + // Enum: [DDDSIT DDASIT DDFSIT DDSFSC DOPSIT DOASIT DOFSIT DOSFSC IDDSIT IDASIT IDFSIT IDSFSC IOPSIT IOASIT IOFSIT IOSFSC] ReServiceCode string `json:"reServiceCode,omitempty"` // Indicates if "Approvals Requested" status is being requested. @@ -471,7 +471,7 @@ var updateMTOServiceItemSITTypeReServiceCodePropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["DDDSIT","DDASIT","DDFSIT","DDSFSC","DOPSIT","DOASIT","DOFSIT","DOSFSC"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["DDDSIT","DDASIT","DDFSIT","DDSFSC","DOPSIT","DOASIT","DOFSIT","DOSFSC","IDDSIT","IDASIT","IDFSIT","IDSFSC","IOPSIT","IOASIT","IOFSIT","IOSFSC"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/primev2api/embedded_spec.go b/pkg/gen/primev2api/embedded_spec.go index f907813b53b..ef97fe89ca4 100644 --- a/pkg/gen/primev2api/embedded_spec.go +++ b/pkg/gen/primev2api/embedded_spec.go @@ -1480,12 +1480,14 @@ func init() { ] }, "MTOServiceItemModelType": { - "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", + "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT\n * IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "MTOServiceItemBasic", "MTOServiceItemOriginSIT", "MTOServiceItemDestSIT", + "MTOServiceItemInternationalOriginSIT", + "MTOServiceItemInternationalDestSIT", "MTOServiceItemShuttle", "MTOServiceItemDomesticCrating", "MTOServiceItemInternationalCrating", @@ -5102,12 +5104,14 @@ func init() { ] }, "MTOServiceItemModelType": { - "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", + "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT\n * IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "MTOServiceItemBasic", "MTOServiceItemOriginSIT", "MTOServiceItemDestSIT", + "MTOServiceItemInternationalOriginSIT", + "MTOServiceItemInternationalDestSIT", "MTOServiceItemShuttle", "MTOServiceItemDomesticCrating", "MTOServiceItemInternationalCrating", diff --git a/pkg/gen/primev2messages/m_t_o_service_item_model_type.go b/pkg/gen/primev2messages/m_t_o_service_item_model_type.go index 58755658344..6df5c56d616 100644 --- a/pkg/gen/primev2messages/m_t_o_service_item_model_type.go +++ b/pkg/gen/primev2messages/m_t_o_service_item_model_type.go @@ -19,6 +19,8 @@ import ( // Using this list, choose the correct modelType in the dropdown, corresponding to the service item type. // - DOFSIT, DOASIT - MTOServiceItemOriginSIT // - DDFSIT, DDASIT - MTOServiceItemDestSIT +// - IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT +// - IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT // - DOSHUT, DDSHUT - MTOServiceItemShuttle // - DCRT, DUCRT - MTOServiceItemDomesticCrating // - ICRT, IUCRT - MTOServiceItemInternationalCrating @@ -49,6 +51,12 @@ const ( // MTOServiceItemModelTypeMTOServiceItemDestSIT captures enum value "MTOServiceItemDestSIT" MTOServiceItemModelTypeMTOServiceItemDestSIT MTOServiceItemModelType = "MTOServiceItemDestSIT" + // MTOServiceItemModelTypeMTOServiceItemInternationalOriginSIT captures enum value "MTOServiceItemInternationalOriginSIT" + MTOServiceItemModelTypeMTOServiceItemInternationalOriginSIT MTOServiceItemModelType = "MTOServiceItemInternationalOriginSIT" + + // MTOServiceItemModelTypeMTOServiceItemInternationalDestSIT captures enum value "MTOServiceItemInternationalDestSIT" + MTOServiceItemModelTypeMTOServiceItemInternationalDestSIT MTOServiceItemModelType = "MTOServiceItemInternationalDestSIT" + // MTOServiceItemModelTypeMTOServiceItemShuttle captures enum value "MTOServiceItemShuttle" MTOServiceItemModelTypeMTOServiceItemShuttle MTOServiceItemModelType = "MTOServiceItemShuttle" @@ -67,7 +75,7 @@ var mTOServiceItemModelTypeEnum []interface{} func init() { var res []MTOServiceItemModelType - if err := json.Unmarshal([]byte(`["MTOServiceItemBasic","MTOServiceItemOriginSIT","MTOServiceItemDestSIT","MTOServiceItemShuttle","MTOServiceItemDomesticCrating","MTOServiceItemInternationalCrating","MTOSerivceItemInternationalFuelSurcharge"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["MTOServiceItemBasic","MTOServiceItemOriginSIT","MTOServiceItemDestSIT","MTOServiceItemInternationalOriginSIT","MTOServiceItemInternationalDestSIT","MTOServiceItemShuttle","MTOServiceItemDomesticCrating","MTOServiceItemInternationalCrating","MTOSerivceItemInternationalFuelSurcharge"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/primev3api/embedded_spec.go b/pkg/gen/primev3api/embedded_spec.go index df6a51d3016..90e5c173345 100644 --- a/pkg/gen/primev3api/embedded_spec.go +++ b/pkg/gen/primev3api/embedded_spec.go @@ -1642,12 +1642,14 @@ func init() { ] }, "MTOServiceItemModelType": { - "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", + "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT\n * IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "MTOServiceItemBasic", "MTOServiceItemOriginSIT", "MTOServiceItemDestSIT", + "MTOServiceItemInternationalOriginSIT", + "MTOServiceItemInternationalDestSIT", "MTOServiceItemShuttle", "MTOServiceItemDomesticCrating", "MTOServiceItemInternationalCrating", @@ -5949,12 +5951,14 @@ func init() { ] }, "MTOServiceItemModelType": { - "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", + "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT\n * IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "MTOServiceItemBasic", "MTOServiceItemOriginSIT", "MTOServiceItemDestSIT", + "MTOServiceItemInternationalOriginSIT", + "MTOServiceItemInternationalDestSIT", "MTOServiceItemShuttle", "MTOServiceItemDomesticCrating", "MTOServiceItemInternationalCrating", diff --git a/pkg/gen/primev3messages/m_t_o_service_item_model_type.go b/pkg/gen/primev3messages/m_t_o_service_item_model_type.go index 50a9d22e418..afb2b2d305b 100644 --- a/pkg/gen/primev3messages/m_t_o_service_item_model_type.go +++ b/pkg/gen/primev3messages/m_t_o_service_item_model_type.go @@ -19,6 +19,8 @@ import ( // Using this list, choose the correct modelType in the dropdown, corresponding to the service item type. // - DOFSIT, DOASIT - MTOServiceItemOriginSIT // - DDFSIT, DDASIT - MTOServiceItemDestSIT +// - IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT +// - IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT // - DOSHUT, DDSHUT - MTOServiceItemShuttle // - DCRT, DUCRT - MTOServiceItemDomesticCrating // - ICRT, IUCRT - MTOServiceItemInternationalCrating @@ -49,6 +51,12 @@ const ( // MTOServiceItemModelTypeMTOServiceItemDestSIT captures enum value "MTOServiceItemDestSIT" MTOServiceItemModelTypeMTOServiceItemDestSIT MTOServiceItemModelType = "MTOServiceItemDestSIT" + // MTOServiceItemModelTypeMTOServiceItemInternationalOriginSIT captures enum value "MTOServiceItemInternationalOriginSIT" + MTOServiceItemModelTypeMTOServiceItemInternationalOriginSIT MTOServiceItemModelType = "MTOServiceItemInternationalOriginSIT" + + // MTOServiceItemModelTypeMTOServiceItemInternationalDestSIT captures enum value "MTOServiceItemInternationalDestSIT" + MTOServiceItemModelTypeMTOServiceItemInternationalDestSIT MTOServiceItemModelType = "MTOServiceItemInternationalDestSIT" + // MTOServiceItemModelTypeMTOServiceItemShuttle captures enum value "MTOServiceItemShuttle" MTOServiceItemModelTypeMTOServiceItemShuttle MTOServiceItemModelType = "MTOServiceItemShuttle" @@ -67,7 +75,7 @@ var mTOServiceItemModelTypeEnum []interface{} func init() { var res []MTOServiceItemModelType - if err := json.Unmarshal([]byte(`["MTOServiceItemBasic","MTOServiceItemOriginSIT","MTOServiceItemDestSIT","MTOServiceItemShuttle","MTOServiceItemDomesticCrating","MTOServiceItemInternationalCrating","MTOSerivceItemInternationalFuelSurcharge"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["MTOServiceItemBasic","MTOServiceItemOriginSIT","MTOServiceItemDestSIT","MTOServiceItemInternationalOriginSIT","MTOServiceItemInternationalDestSIT","MTOServiceItemShuttle","MTOServiceItemDomesticCrating","MTOServiceItemInternationalCrating","MTOSerivceItemInternationalFuelSurcharge"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/handlers/primeapi/mto_service_item.go b/pkg/handlers/primeapi/mto_service_item.go index 7dad26d373b..b51255d2e01 100644 --- a/pkg/handlers/primeapi/mto_service_item.go +++ b/pkg/handlers/primeapi/mto_service_item.go @@ -25,11 +25,13 @@ import ( // THIS WILL NEED TO BE UPDATED AS WE CONTINUE TO ADD MORE SERVICE ITEMS. // We will eventually remove this when all service items are added. var CreateableServiceItemMap = map[primemessages.MTOServiceItemModelType]bool{ - primemessages.MTOServiceItemModelTypeMTOServiceItemOriginSIT: true, - primemessages.MTOServiceItemModelTypeMTOServiceItemDestSIT: true, - primemessages.MTOServiceItemModelTypeMTOServiceItemShuttle: true, - primemessages.MTOServiceItemModelTypeMTOServiceItemDomesticCrating: true, - primemessages.MTOServiceItemModelTypeMTOServiceItemInternationalCrating: true, + primemessages.MTOServiceItemModelTypeMTOServiceItemOriginSIT: true, + primemessages.MTOServiceItemModelTypeMTOServiceItemDestSIT: true, + primemessages.MTOServiceItemModelTypeMTOServiceItemInternationalOriginSIT: true, + primemessages.MTOServiceItemModelTypeMTOServiceItemInternationalDestSIT: true, + primemessages.MTOServiceItemModelTypeMTOServiceItemShuttle: true, + primemessages.MTOServiceItemModelTypeMTOServiceItemDomesticCrating: true, + primemessages.MTOServiceItemModelTypeMTOServiceItemInternationalCrating: true, } // CreateMTOServiceItemHandler is the handler to create MTO service items diff --git a/pkg/handlers/primeapi/payloads/payload_to_model.go b/pkg/handlers/primeapi/payloads/payload_to_model.go index 08a64b02b82..a53262533ca 100644 --- a/pkg/handlers/primeapi/payloads/payload_to_model.go +++ b/pkg/handlers/primeapi/payloads/payload_to_model.go @@ -470,6 +470,49 @@ func MTOServiceItemModel(mtoServiceItem primemessages.MTOServiceItem) (*models.M model.SITOriginHHGActualAddressID = &model.SITOriginHHGActualAddress.ID } + case primemessages.MTOServiceItemModelTypeMTOServiceItemInternationalOriginSIT: + + originsit := mtoServiceItem.(*primemessages.MTOServiceItemInternationalOriginSIT) + + if originsit.ReServiceCode != nil { + model.ReService.Code = models.ReServiceCode(*originsit.ReServiceCode) + } + + model.Reason = originsit.Reason + // Check for reason required field on a DDFSIT + if model.ReService.Code == models.ReServiceCodeDOASIT { + reasonVerrs := validateReasonInternationalOriginSIT(*originsit) + + if reasonVerrs.HasAny() { + return nil, reasonVerrs + } + } + + if model.ReService.Code == models.ReServiceCodeDOFSIT { + reasonVerrs := validateReasonInternationalOriginSIT(*originsit) + + if reasonVerrs.HasAny() { + return nil, reasonVerrs + } + } + + sitEntryDate := handlers.FmtDatePtrToPopPtr(originsit.SitEntryDate) + + if sitEntryDate != nil { + model.SITEntryDate = sitEntryDate + } + + if originsit.SitDepartureDate != nil { + model.SITDepartureDate = handlers.FmtDatePtrToPopPtr(originsit.SitDepartureDate) + } + + model.SITPostalCode = originsit.SitPostalCode + + model.SITOriginHHGActualAddress = AddressModel(originsit.SitHHGActualOrigin) + if model.SITOriginHHGActualAddress != nil { + model.SITOriginHHGActualAddressID = &model.SITOriginHHGActualAddress.ID + } + case primemessages.MTOServiceItemModelTypeMTOServiceItemDestSIT: destsit := mtoServiceItem.(*primemessages.MTOServiceItemDestSIT) @@ -529,6 +572,65 @@ func MTOServiceItemModel(mtoServiceItem primemessages.MTOServiceItem) (*models.M model.SITDestinationFinalAddressID = &model.SITDestinationFinalAddress.ID } + case primemessages.MTOServiceItemModelTypeMTOServiceItemInternationalDestSIT: + destsit := mtoServiceItem.(*primemessages.MTOServiceItemInternationalDestSIT) + + if destsit.ReServiceCode != nil { + model.ReService.Code = models.ReServiceCode(*destsit.ReServiceCode) + + } + + model.Reason = destsit.Reason + sitEntryDate := handlers.FmtDatePtrToPopPtr(destsit.SitEntryDate) + + // Check for required fields on a DDFSIT + if model.ReService.Code == models.ReServiceCodeDDFSIT { + verrs := validateIDFSITForCreate(*destsit) + reasonVerrs := validateReasonInternationalDestSIT(*destsit) + + if verrs.HasAny() { + return nil, verrs + } + + if reasonVerrs.HasAny() { + return nil, reasonVerrs + } + } + + var customerContacts models.MTOServiceItemCustomerContacts + + if destsit.TimeMilitary1 != nil && destsit.FirstAvailableDeliveryDate1 != nil && destsit.DateOfContact1 != nil { + customerContacts = append(customerContacts, models.MTOServiceItemCustomerContact{ + Type: models.CustomerContactTypeFirst, + DateOfContact: time.Time(*destsit.DateOfContact1), + TimeMilitary: *destsit.TimeMilitary1, + FirstAvailableDeliveryDate: time.Time(*destsit.FirstAvailableDeliveryDate1), + }) + } + if destsit.TimeMilitary2 != nil && destsit.FirstAvailableDeliveryDate2 != nil && destsit.DateOfContact2 != nil { + customerContacts = append(customerContacts, models.MTOServiceItemCustomerContact{ + Type: models.CustomerContactTypeSecond, + DateOfContact: time.Time(*destsit.DateOfContact2), + TimeMilitary: *destsit.TimeMilitary2, + FirstAvailableDeliveryDate: time.Time(*destsit.FirstAvailableDeliveryDate2), + }) + } + + model.CustomerContacts = customerContacts + + if sitEntryDate != nil { + model.SITEntryDate = sitEntryDate + } + + if destsit.SitDepartureDate != nil { + model.SITDepartureDate = handlers.FmtDatePtrToPopPtr(destsit.SitDepartureDate) + } + + model.SITDestinationFinalAddress = AddressModel(destsit.SitDestinationFinalAddress) + if model.SITDestinationFinalAddress != nil { + model.SITDestinationFinalAddressID = &model.SITDestinationFinalAddress.ID + } + case primemessages.MTOServiceItemModelTypeMTOServiceItemShuttle: shuttleService := mtoServiceItem.(*primemessages.MTOServiceItemShuttle) // values to get from payload @@ -830,6 +932,31 @@ func validateDDFSITForCreate(m primemessages.MTOServiceItemDestSIT) *validate.Er return verrs } +// validateIDFSITForCreate validates DDFSIT service item has all required fields +func validateIDFSITForCreate(m primemessages.MTOServiceItemInternationalDestSIT) *validate.Errors { + verrs := validate.NewErrors() + + if m.FirstAvailableDeliveryDate1 == nil && m.DateOfContact1 != nil && m.TimeMilitary1 != nil { + verrs.Add("firstAvailableDeliveryDate1", "firstAvailableDeliveryDate1, dateOfContact1, and timeMilitary1 must be provided together in body.") + } + if m.DateOfContact1 == nil && m.TimeMilitary1 != nil && m.FirstAvailableDeliveryDate1 != nil { + verrs.Add("DateOfContact1", "dateOfContact1, timeMilitary1, and firstAvailableDeliveryDate1 must be provided together in body.") + } + if m.TimeMilitary1 == nil && m.DateOfContact1 != nil && m.FirstAvailableDeliveryDate1 != nil { + verrs.Add("timeMilitary1", "timeMilitary1, dateOfContact1, and firstAvailableDeliveryDate1 must be provided together in body.") + } + if m.FirstAvailableDeliveryDate2 == nil && m.DateOfContact2 != nil && m.TimeMilitary2 != nil { + verrs.Add("firstAvailableDeliveryDate2", "firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 must be provided together in body.") + } + if m.DateOfContact2 == nil && m.TimeMilitary2 != nil && m.FirstAvailableDeliveryDate2 != nil { + verrs.Add("DateOfContact1", "dateOfContact2, firstAvailableDeliveryDate2, and timeMilitary2 must be provided together in body.") + } + if m.TimeMilitary2 == nil && m.DateOfContact2 != nil && m.FirstAvailableDeliveryDate2 != nil { + verrs.Add("timeMilitary2", "timeMilitary2, firstAvailableDeliveryDate2, and dateOfContact2 must be provided together in body.") + } + return verrs +} + // validateDestSITForUpdate validates DDDSIT service item has all required fields func validateDestSITForUpdate(m primemessages.UpdateMTOServiceItemSIT) *validate.Errors { verrs := validate.NewErrors() @@ -865,6 +992,16 @@ func validateReasonDestSIT(m primemessages.MTOServiceItemDestSIT) *validate.Erro return verrs } +// validateReasonInternationalDestSIT validates that International Destination SIT service items have required Reason field +func validateReasonInternationalDestSIT(m primemessages.MTOServiceItemInternationalDestSIT) *validate.Errors { + verrs := validate.NewErrors() + + if m.Reason == nil || m.Reason == models.StringPointer("") { + verrs.Add("reason", "reason is required in body.") + } + return verrs +} + // validateReasonOriginSIT validates that Origin SIT service items have required Reason field func validateReasonOriginSIT(m primemessages.MTOServiceItemOriginSIT) *validate.Errors { verrs := validate.NewErrors() @@ -874,3 +1011,13 @@ func validateReasonOriginSIT(m primemessages.MTOServiceItemOriginSIT) *validate. } return verrs } + +// validateReasonInternationalOriginSIT validates that International Origin SIT service items have required Reason field +func validateReasonInternationalOriginSIT(m primemessages.MTOServiceItemInternationalOriginSIT) *validate.Errors { + verrs := validate.NewErrors() + + if m.Reason == nil || m.Reason == models.StringPointer("") { + verrs.Add("reason", "reason is required in body.") + } + return verrs +} diff --git a/pkg/handlers/primeapiv3/payloads/model_to_payload.go b/pkg/handlers/primeapiv3/payloads/model_to_payload.go index fc11ef66bac..f3e438119a7 100644 --- a/pkg/handlers/primeapiv3/payloads/model_to_payload.go +++ b/pkg/handlers/primeapiv3/payloads/model_to_payload.go @@ -737,6 +737,20 @@ func MTOServiceItem(mtoServiceItem *models.MTOServiceItem) primev3messages.MTOSe SitHHGActualOrigin: Address(mtoServiceItem.SITOriginHHGActualAddress), SitHHGOriginalOrigin: Address(mtoServiceItem.SITOriginHHGOriginalAddress), } + case models.ReServiceCodeIOFSIT, models.ReServiceCodeIOASIT, models.ReServiceCodeIOPSIT, models.ReServiceCodeIOSFSC: + var sitDepartureDate time.Time + if mtoServiceItem.SITDepartureDate != nil { + sitDepartureDate = *mtoServiceItem.SITDepartureDate + } + payload = &primev3messages.MTOServiceItemOriginSIT{ + ReServiceCode: handlers.FmtString(string(mtoServiceItem.ReService.Code)), + Reason: mtoServiceItem.Reason, + SitDepartureDate: handlers.FmtDate(sitDepartureDate), + SitEntryDate: handlers.FmtDatePtr(mtoServiceItem.SITEntryDate), + SitPostalCode: mtoServiceItem.SITPostalCode, + SitHHGActualOrigin: Address(mtoServiceItem.SITOriginHHGActualAddress), + SitHHGOriginalOrigin: Address(mtoServiceItem.SITOriginHHGOriginalAddress), + } case models.ReServiceCodeDDFSIT, models.ReServiceCodeDDASIT, models.ReServiceCodeDDDSIT, models.ReServiceCodeDDSFSC: var sitDepartureDate, firstAvailableDeliveryDate1, firstAvailableDeliveryDate2, dateOfContact1, dateOfContact2 time.Time var timeMilitary1, timeMilitary2 *string @@ -781,7 +795,50 @@ func MTOServiceItem(mtoServiceItem *models.MTOServiceItem) primev3messages.MTOSe SitCustomerContacted: handlers.FmtDatePtr(mtoServiceItem.SITCustomerContacted), SitRequestedDelivery: handlers.FmtDatePtr(mtoServiceItem.SITRequestedDelivery), } + case models.ReServiceCodeIDFSIT, models.ReServiceCodeIDASIT, models.ReServiceCodeIDDSIT, models.ReServiceCodeIDSFSC: + var sitDepartureDate, firstAvailableDeliveryDate1, firstAvailableDeliveryDate2, dateOfContact1, dateOfContact2 time.Time + var timeMilitary1, timeMilitary2 *string + + if mtoServiceItem.SITDepartureDate != nil { + sitDepartureDate = *mtoServiceItem.SITDepartureDate + } + + firstContact := GetCustomerContact(mtoServiceItem.CustomerContacts, models.CustomerContactTypeFirst) + secondContact := GetCustomerContact(mtoServiceItem.CustomerContacts, models.CustomerContactTypeSecond) + timeMilitary1 = &firstContact.TimeMilitary + timeMilitary2 = &secondContact.TimeMilitary + + if !firstContact.DateOfContact.IsZero() { + dateOfContact1 = firstContact.DateOfContact + } + if !secondContact.DateOfContact.IsZero() { + dateOfContact2 = secondContact.DateOfContact + } + + if !firstContact.FirstAvailableDeliveryDate.IsZero() { + firstAvailableDeliveryDate1 = firstContact.FirstAvailableDeliveryDate + } + + if !secondContact.FirstAvailableDeliveryDate.IsZero() { + firstAvailableDeliveryDate2 = secondContact.FirstAvailableDeliveryDate + } + + payload = &primev3messages.MTOServiceItemDestSIT{ + ReServiceCode: handlers.FmtString(string(mtoServiceItem.ReService.Code)), + Reason: mtoServiceItem.Reason, + DateOfContact1: handlers.FmtDate(dateOfContact1), + TimeMilitary1: handlers.FmtStringPtrNonEmpty(timeMilitary1), + FirstAvailableDeliveryDate1: handlers.FmtDate(firstAvailableDeliveryDate1), + DateOfContact2: handlers.FmtDate(dateOfContact2), + TimeMilitary2: handlers.FmtStringPtrNonEmpty(timeMilitary2), + FirstAvailableDeliveryDate2: handlers.FmtDate(firstAvailableDeliveryDate2), + SitDepartureDate: handlers.FmtDate(sitDepartureDate), + SitEntryDate: handlers.FmtDatePtr(mtoServiceItem.SITEntryDate), + SitDestinationFinalAddress: Address(mtoServiceItem.SITDestinationFinalAddress), + SitCustomerContacted: handlers.FmtDatePtr(mtoServiceItem.SITCustomerContacted), + SitRequestedDelivery: handlers.FmtDatePtr(mtoServiceItem.SITRequestedDelivery), + } case models.ReServiceCodeDCRT, models.ReServiceCodeDUCRT: item := GetDimension(mtoServiceItem.Dimensions, models.DimensionTypeItem) crate := GetDimension(mtoServiceItem.Dimensions, models.DimensionTypeCrate) diff --git a/pkg/models/re_service.go b/pkg/models/re_service.go index 5fc9d9b3e75..5822b7f312d 100644 --- a/pkg/models/re_service.go +++ b/pkg/models/re_service.go @@ -83,6 +83,8 @@ const ( ReServiceCodeIDDSIT ReServiceCode = "IDDSIT" // ReServiceCodeIDFSIT International destination 1st day SIT ReServiceCodeIDFSIT ReServiceCode = "IDFSIT" + // ReServiceCodeIDSFSC International destination SIT FSC + ReServiceCodeIDSFSC ReServiceCode = "IDSFSC" // ReServiceCodeIDSHUT International destination shuttle service ReServiceCodeIDSHUT ReServiceCode = "IDSHUT" // ReServiceCodeIHPK International HHG pack @@ -105,6 +107,8 @@ const ( ReServiceCodeIOOUB ReServiceCode = "IOOUB" // ReServiceCodeIOPSIT International origin SIT pickup ReServiceCodeIOPSIT ReServiceCode = "IOPSIT" + // ReServiceCodeIOSFSC International origin SIT FSC + ReServiceCodeIOSFSC ReServiceCode = "IOSFSC" // ReServiceCodeIOSHUT International origin shuttle service ReServiceCodeIOSHUT ReServiceCode = "IOSHUT" // ReServiceCodeIUBPK International UB pack diff --git a/pkg/services/mto_service_item/mto_service_item_creator.go b/pkg/services/mto_service_item/mto_service_item_creator.go index 6bac7e4ec89..405e4d35076 100644 --- a/pkg/services/mto_service_item/mto_service_item_creator.go +++ b/pkg/services/mto_service_item/mto_service_item_creator.go @@ -9,6 +9,7 @@ import ( "github.com/gobuffalo/validate/v3" "github.com/gofrs/uuid" "go.uber.org/zap" + "golang.org/x/exp/slices" "github.com/transcom/mymove/pkg/appcontext" "github.com/transcom/mymove/pkg/apperror" @@ -444,25 +445,43 @@ func (o *mtoServiceItemCreator) CreateMTOServiceItem(appCtx appcontext.AppContex } } + err = validateSITServiceItem(mtoShipment, *serviceItem) + if err != nil { + return nil, nil, err + } + // checking to see if the service item being created is a destination SIT // if so, we want the destination address to be the same as the shipment's // which will later populate the additional dest SIT service items as well - if serviceItem.ReService.Code == models.ReServiceCodeDDFSIT && mtoShipment.DestinationAddressID != nil { + if (serviceItem.ReService.Code == models.ReServiceCodeDDFSIT || serviceItem.ReService.Code == models.ReServiceCodeIDFSIT) && + mtoShipment.DestinationAddressID != nil { serviceItem.SITDestinationFinalAddress = mtoShipment.DestinationAddress serviceItem.SITDestinationFinalAddressID = mtoShipment.DestinationAddressID } - if serviceItem.ReService.Code == models.ReServiceCodeDOASIT { + if serviceItem.ReService.Code == models.ReServiceCodeDOASIT || serviceItem.ReService.Code == models.ReServiceCodeIOASIT { + // validation mappings // DOASIT must be associated with shipment that has DOFSIT - serviceItem, err = o.validateSITStandaloneServiceItem(appCtx, serviceItem, models.ReServiceCodeDOFSIT) + // IOASIT must be associated with shipment that has IOFSIT + m := make(map[models.ReServiceCode]models.ReServiceCode) + m[models.ReServiceCodeDOASIT] = models.ReServiceCodeDOFSIT + m[models.ReServiceCodeIOASIT] = models.ReServiceCodeIOFSIT + + serviceItem, err = o.validateSITStandaloneServiceItem(appCtx, serviceItem, m[serviceItem.ReService.Code]) if err != nil { return nil, nil, err } } - if serviceItem.ReService.Code == models.ReServiceCodeDDASIT { + if serviceItem.ReService.Code == models.ReServiceCodeDDASIT || serviceItem.ReService.Code == models.ReServiceCodeIDASIT { + // validation mappings // DDASIT must be associated with shipment that has DDFSIT - serviceItem, err = o.validateSITStandaloneServiceItem(appCtx, serviceItem, models.ReServiceCodeDDFSIT) + // IDASIT must be associated with shipment that has IDFSIT + m := make(map[models.ReServiceCode]models.ReServiceCode) + m[models.ReServiceCodeDDASIT] = models.ReServiceCodeDDFSIT + m[models.ReServiceCodeIDASIT] = models.ReServiceCodeIDFSIT + + serviceItem, err = o.validateSITStandaloneServiceItem(appCtx, serviceItem, m[serviceItem.ReService.Code]) if err != nil { return nil, nil, err } @@ -477,7 +496,9 @@ func (o *mtoServiceItemCreator) CreateMTOServiceItem(appCtx appcontext.AppContex } if serviceItem.ReService.Code == models.ReServiceCodeDDDSIT || serviceItem.ReService.Code == models.ReServiceCodeDOPSIT || - serviceItem.ReService.Code == models.ReServiceCodeDDSFSC || serviceItem.ReService.Code == models.ReServiceCodeDOSFSC { + serviceItem.ReService.Code == models.ReServiceCodeDDSFSC || serviceItem.ReService.Code == models.ReServiceCodeDOSFSC || + serviceItem.ReService.Code == models.ReServiceCodeIDDSIT || serviceItem.ReService.Code == models.ReServiceCodeIOPSIT || + serviceItem.ReService.Code == models.ReServiceCodeIDSFSC || serviceItem.ReService.Code == models.ReServiceCodeIOSFSC { verrs = validate.NewErrors() verrs.Add("reServiceCode", fmt.Sprintf("%s cannot be created", serviceItem.ReService.Code)) return nil, nil, apperror.NewInvalidInputError(serviceItem.ID, nil, verrs, @@ -485,15 +506,19 @@ func (o *mtoServiceItemCreator) CreateMTOServiceItem(appCtx appcontext.AppContex } updateShipmentPickupAddress := false - if serviceItem.ReService.Code == models.ReServiceCodeDDFSIT || serviceItem.ReService.Code == models.ReServiceCodeDOFSIT { + if serviceItem.ReService.Code == models.ReServiceCodeDDFSIT || + serviceItem.ReService.Code == models.ReServiceCodeDOFSIT || + serviceItem.ReService.Code == models.ReServiceCodeIDFSIT || + serviceItem.ReService.Code == models.ReServiceCodeIOFSIT { extraServiceItems, errSIT := o.validateFirstDaySITServiceItem(appCtx, serviceItem) if errSIT != nil { return nil, nil, errSIT } - // update HHG origin address for ReServiceCodeDOFSIT service item - if serviceItem.ReService.Code == models.ReServiceCodeDOFSIT { - // When creating a DOFSIT, the prime must provide an HHG actual address for the move/shift in origin (pickup address) + // update HHG origin address for ReServiceCodeDOFSIT/ReServiceCodeIOFSIT service item + if serviceItem.ReService.Code == models.ReServiceCodeDOFSIT || + serviceItem.ReService.Code == models.ReServiceCodeIOFSIT { + // When creating a DOFSIT/IOFSIT, the prime must provide an HHG actual address for the move/shift in origin (pickup address) if serviceItem.SITOriginHHGActualAddress == nil { verrs = validate.NewErrors() verrs.Add("reServiceCode", fmt.Sprintf("%s cannot be created", serviceItem.ReService.Code)) @@ -542,13 +567,16 @@ func (o *mtoServiceItemCreator) CreateMTOServiceItem(appCtx appcontext.AppContex // changes were made to the shipment, needs to be saved to the database updateShipmentPickupAddress = true - // Find the DOPSIT service item and update the SIT related address fields. These fields - // will be used for pricing when a payment request is created for DOPSIT + // Find the DOPSIT/IOPSIT service item and update the SIT related address fields. These fields + // will be used for pricing when a payment request is created for DOPSIT/IOPSIT for itemIndex := range *extraServiceItems { extraServiceItem := &(*extraServiceItems)[itemIndex] if extraServiceItem.ReService.Code == models.ReServiceCodeDOPSIT || extraServiceItem.ReService.Code == models.ReServiceCodeDOASIT || - extraServiceItem.ReService.Code == models.ReServiceCodeDOSFSC { + extraServiceItem.ReService.Code == models.ReServiceCodeDOSFSC || + extraServiceItem.ReService.Code == models.ReServiceCodeIOPSIT || + extraServiceItem.ReService.Code == models.ReServiceCodeIOASIT || + extraServiceItem.ReService.Code == models.ReServiceCodeIOSFSC { extraServiceItem.SITOriginHHGActualAddress = serviceItem.SITOriginHHGActualAddress extraServiceItem.SITOriginHHGActualAddressID = serviceItem.SITOriginHHGActualAddressID extraServiceItem.SITOriginHHGOriginalAddress = serviceItem.SITOriginHHGOriginalAddress @@ -558,12 +586,17 @@ func (o *mtoServiceItemCreator) CreateMTOServiceItem(appCtx appcontext.AppContex } // make sure SITDestinationFinalAddress is the same for all destination SIT related service item - if serviceItem.ReService.Code == models.ReServiceCodeDDFSIT && serviceItem.SITDestinationFinalAddress != nil { + if (serviceItem.ReService.Code == models.ReServiceCodeDDFSIT || serviceItem.ReService.Code == models.ReServiceCodeIDFSIT) && + serviceItem.SITDestinationFinalAddress != nil { for itemIndex := range *extraServiceItems { extraServiceItem := &(*extraServiceItems)[itemIndex] + // handle both domestic and internationl(OCONUS) if extraServiceItem.ReService.Code == models.ReServiceCodeDDDSIT || extraServiceItem.ReService.Code == models.ReServiceCodeDDASIT || - extraServiceItem.ReService.Code == models.ReServiceCodeDDSFSC { + extraServiceItem.ReService.Code == models.ReServiceCodeDDSFSC || + extraServiceItem.ReService.Code == models.ReServiceCodeIDDSIT || + extraServiceItem.ReService.Code == models.ReServiceCodeIDASIT || + extraServiceItem.ReService.Code == models.ReServiceCodeIDSFSC { extraServiceItem.SITDestinationFinalAddress = serviceItem.SITDestinationFinalAddress extraServiceItem.SITDestinationFinalAddressID = serviceItem.SITDestinationFinalAddressID } @@ -917,6 +950,10 @@ func (o *mtoServiceItemCreator) validateFirstDaySITServiceItem(appCtx appcontext reServiceCodes = append(reServiceCodes, models.ReServiceCodeDDASIT, models.ReServiceCodeDDDSIT, models.ReServiceCodeDDSFSC) case models.ReServiceCodeDOFSIT: reServiceCodes = append(reServiceCodes, models.ReServiceCodeDOASIT, models.ReServiceCodeDOPSIT, models.ReServiceCodeDOSFSC) + case models.ReServiceCodeIDFSIT: + reServiceCodes = append(reServiceCodes, models.ReServiceCodeIDASIT, models.ReServiceCodeIDDSIT, models.ReServiceCodeIDSFSC) + case models.ReServiceCodeIOFSIT: + reServiceCodes = append(reServiceCodes, models.ReServiceCodeIOASIT, models.ReServiceCodeIOPSIT, models.ReServiceCodeIOSFSC) default: verrs := validate.NewErrors() verrs.Add("reServiceCode", fmt.Sprintf("%s invalid code", serviceItem.ReService.Code)) @@ -937,3 +974,42 @@ func (o *mtoServiceItemCreator) validateFirstDaySITServiceItem(appCtx appcontext return &extraServiceItems, nil } + +func validateSITServiceItem(mtoShipment models.MTOShipment, serviceItem models.MTOServiceItem) error { + marketToAllowableReServiceCodesMap := make(map[models.MarketCode][]models.ReServiceCode) + marketToAllowableReServiceCodesMap[models.MarketCodeDomestic] = []models.ReServiceCode{ + models.ReServiceCodeDDDSIT, + models.ReServiceCodeDDASIT, + models.ReServiceCodeDDFSIT, + models.ReServiceCodeDDSFSC, + models.ReServiceCodeDOPSIT, + models.ReServiceCodeDOFSIT, + models.ReServiceCodeDOASIT, + models.ReServiceCodeDOSFSC, + } + marketToAllowableReServiceCodesMap[models.MarketCodeInternational] = []models.ReServiceCode{ + models.ReServiceCodeIDDSIT, + models.ReServiceCodeIDASIT, + models.ReServiceCodeIDFSIT, + models.ReServiceCodeIDSFSC, + models.ReServiceCodeIOPSIT, + models.ReServiceCodeIOFSIT, + models.ReServiceCodeIOASIT, + models.ReServiceCodeIOSFSC, + } + + values, contains := marketToAllowableReServiceCodesMap[mtoShipment.MarketCode] + if !contains { + return apperror.NewNotImplementedError(fmt.Sprintf("validateSITServiceItem - MarketCode: %s is not implemented", mtoShipment.MarketCode)) + } + + // check if there is miss match of ReServiceCode and marketCode of shipment(domestic or international). ie..cannot send in + // IOFSIT(international) when shipment is domestic. + if !slices.Contains(values, serviceItem.ReService.Code) { + return apperror.NewConflictError( + serviceItem.MoveTaskOrderID, + fmt.Sprintf("Cannot create service item due to mismatched market to provided ReServiceCode. Market:%s, ServiceItem.ReService.Code:%s", mtoShipment.MarketCode, serviceItem.ReService.Code), + ) + } + return nil +} diff --git a/pkg/services/mto_service_item/mto_service_item_validators.go b/pkg/services/mto_service_item/mto_service_item_validators.go index e6111caa531..af1c8783993 100644 --- a/pkg/services/mto_service_item/mto_service_item_validators.go +++ b/pkg/services/mto_service_item/mto_service_item_validators.go @@ -43,6 +43,14 @@ var allSITServiceItemsToCheck = []models.ReServiceCode{ models.ReServiceCodeDOFSIT, models.ReServiceCodeDOASIT, models.ReServiceCodeDOSFSC, + models.ReServiceCodeIDDSIT, + models.ReServiceCodeIDASIT, + models.ReServiceCodeIDFSIT, + models.ReServiceCodeIDSFSC, + models.ReServiceCodeIOPSIT, + models.ReServiceCodeIOFSIT, + models.ReServiceCodeIOASIT, + models.ReServiceCodeIOSFSC, } var destSITServiceItems = []models.ReServiceCode{ diff --git a/pkg/services/sit_entry_date_update/sit_entry_date_updater.go b/pkg/services/sit_entry_date_update/sit_entry_date_updater.go index 61bc78bb988..0e9d3d82397 100644 --- a/pkg/services/sit_entry_date_update/sit_entry_date_updater.go +++ b/pkg/services/sit_entry_date_update/sit_entry_date_updater.go @@ -52,9 +52,10 @@ func (p sitEntryDateUpdater) UpdateSitEntryDate(appCtx appcontext.AppContext, s return nil, apperror.NewQueryError("Shipment", err, "") } - // the service code can either be DOFSIT or DDFSIT + // the service code can either be DOFSIT/DDFSIT or IOFSIT/IDFSIT serviceItemCode := serviceItem.ReService.Code - if serviceItemCode != models.ReServiceCodeDOFSIT && serviceItemCode != models.ReServiceCodeDDFSIT { + if serviceItemCode != models.ReServiceCodeDOFSIT && serviceItemCode != models.ReServiceCodeDDFSIT && + serviceItemCode != models.ReServiceCodeIOFSIT && serviceItemCode != models.ReServiceCodeIDFSIT { return nil, apperror.NewUnprocessableEntityError(string(serviceItemCode) + "You cannot change the SIT entry date of this service item.") } @@ -62,16 +63,16 @@ func (p sitEntryDateUpdater) UpdateSitEntryDate(appCtx appcontext.AppContext, s // then looking for the sister service item of add'l days // once found, we'll set the value of variable to that service item // so now we have the 1st day of SIT service item & the add'l days SIT service item - if serviceItemCode == models.ReServiceCodeDOFSIT { + if serviceItemCode == models.ReServiceCodeDOFSIT || serviceItemCode == models.ReServiceCodeIOFSIT { for _, si := range shipment.MTOServiceItems { - if si.ReService.Code == models.ReServiceCodeDOASIT { + if si.ReService.Code == models.ReServiceCodeDOASIT || si.ReService.Code == models.ReServiceCodeIOASIT { serviceItemAdditionalDays = si break } } - } else if serviceItemCode == models.ReServiceCodeDDFSIT { + } else if serviceItemCode == models.ReServiceCodeDDFSIT || serviceItemCode == models.ReServiceCodeIDFSIT { for _, si := range shipment.MTOServiceItems { - if si.ReService.Code == models.ReServiceCodeDDASIT { + if si.ReService.Code == models.ReServiceCodeDDASIT || si.ReService.Code == models.ReServiceCodeIDASIT { serviceItemAdditionalDays = si break } diff --git a/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx b/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx index 689a790ca64..a1c3bd4a950 100644 --- a/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx +++ b/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx @@ -39,6 +39,7 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai 'Customer contact 1': '-', }); const numberOfDaysApprovedForDOASIT = shipment.sitDaysAllowance ? shipment.sitDaysAllowance - 1 : 0; + const numberOfDaysApprovedForIOASIT = shipment.sitDaysAllowance ? shipment.sitDaysAllowance - 1 : 0; const sitEndDate = sitStatus && sitStatus.currentSIT?.sitAuthorizedEndDate && @@ -50,7 +51,7 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai return (
- {code === 'DDFSIT' + {code === 'DDFSIT' || code === 'IDFSIT' ? generateDetailText({ 'Original Delivery Address': originalDeliveryAddress ? formatCityStateAndPostalCode(originalDeliveryAddress) @@ -87,7 +88,36 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai ) : null} )} - {code === 'DDSFSC' + {code === 'IDASIT' && ( + <> + {generateDetailText( + { + 'Original Delivery Address': originalDeliveryAddress + ? formatCityStateAndPostalCode(originalDeliveryAddress) + : '-', + "Add'l SIT Start Date": details.sitEntryDate + ? moment.utc(details.sitEntryDate).add(1, 'days').format('DD MMM YYYY') + : '-', + '# of days approved for': shipment.sitDaysAllowance ? `${numberOfDaysApprovedForIOASIT} days` : '-', + 'SIT expiration date': sitEndDate || '-', + }, + id, + )} + {!isEmpty(serviceRequestDocUploads) ? ( +
+

Download service item documentation:

+ {serviceRequestDocUploads.map((file) => ( +
+ + {trimFileName(file.filename)} + +
+ ))} +
+ ) : null} + + )} + {code === 'DDSFSC' || code === 'IDSFSC' ? generateDetailText( { 'Original Delivery Address': originalDeliveryAddress @@ -140,6 +170,44 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai ) : null} )} + {code === 'IDDSIT' && ( + <> + {generateDetailText( + { + 'Original Delivery Address': originalDeliveryAddress + ? formatCityStateAndPostalCode(originalDeliveryAddress) + : '-', + 'Final Delivery Address': + details.sitDestinationFinalAddress && details.status !== 'SUBMITTED' + ? formatCityStateAndPostalCode(details.sitDestinationFinalAddress) + : '-', + 'Delivery miles out of SIT': details.sitDeliveryMiles ? details.sitDeliveryMiles : '-', + 'Customer contacted homesafe': details.sitCustomerContacted + ? formatDateWithUTC(details.sitCustomerContacted, 'DD MMM YYYY') + : '-', + 'Customer requested delivery date': details.sitRequestedDelivery + ? formatDateWithUTC(details.sitRequestedDelivery, 'DD MMM YYYY') + : '-', + 'SIT departure date': details.sitDepartureDate + ? formatDateWithUTC(details.sitDepartureDate, 'DD MMM YYYY') + : '-', + }, + id, + )} + {!isEmpty(serviceRequestDocUploads) ? ( +
+

Download service item documentation:

+ {serviceRequestDocUploads.map((file) => ( +
+ + {trimFileName(file.filename)} + +
+ ))} +
+ ) : null} + + )} {code === 'DDFSIT' && ( <> {!isEmpty(sortedCustomerContacts) @@ -188,7 +256,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s let detailSection; switch (code) { - case 'DOFSIT': { + case 'DOFSIT': + case 'IOFSIT': { detailSection = (
@@ -221,8 +290,9 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DOASIT': { - const numberOfDaysApprovedForDOASIT = shipment.sitDaysAllowance ? shipment.sitDaysAllowance - 1 : 0; + case 'DOASIT': + case 'IOASIT': { + const numberOfDaysApprovedForIOASIT = shipment.sitDaysAllowance ? shipment.sitDaysAllowance - 1 : 0; const sitEndDate = sitStatus && sitStatus.currentSIT?.sitAuthorizedEndDate && @@ -239,7 +309,7 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s "Add'l SIT Start Date": details.sitEntryDate ? moment.utc(details.sitEntryDate).add(1, 'days').format('DD MMM YYYY') : '-', - '# of days approved for': shipment.sitDaysAllowance ? `${numberOfDaysApprovedForDOASIT} days` : '-', + '# of days approved for': shipment.sitDaysAllowance ? `${numberOfDaysApprovedForIOASIT} days` : '-', 'SIT expiration date': sitEndDate || '-', 'Customer contacted homesafe': details.sitCustomerContacted ? formatDateWithUTC(details.sitCustomerContacted, 'DD MMM YYYY') @@ -272,7 +342,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DOPSIT': { + case 'DOPSIT': + case 'IOPSIT': { detailSection = (
@@ -307,7 +378,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DOSFSC': { + case 'DOSFSC': + case 'IOSFSC': { detailSection = (
@@ -343,7 +415,9 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s break; } case 'DDFSIT': - case 'DDASIT': { + case 'DDASIT': + case 'IDFSIT': + case 'IDASIT': { detailSection = generateDestinationSITDetailSection( id, serviceRequestDocUploads, @@ -354,7 +428,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DDDSIT': { + case 'DDDSIT': + case 'IDDSIT': { detailSection = generateDestinationSITDetailSection( id, serviceRequestDocUploads, @@ -365,7 +440,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DDSFSC': { + case 'DDSFSC': + case 'IDSFSC': { detailSection = generateDestinationSITDetailSection( id, serviceRequestDocUploads, diff --git a/src/components/Office/ServiceItemsTable/ServiceItemsTable.jsx b/src/components/Office/ServiceItemsTable/ServiceItemsTable.jsx index 1b8aeb3383c..2c10239a2a0 100644 --- a/src/components/Office/ServiceItemsTable/ServiceItemsTable.jsx +++ b/src/components/Office/ServiceItemsTable/ServiceItemsTable.jsx @@ -305,7 +305,7 @@ const ServiceItemsTable = ({ className="text-blue usa-button--unstyled margin-left-1" disabled={hasPaymentRequestBeenMade || isMoveLocked} onClick={() => { - if (code === 'DDFSIT' || code === 'DOFSIT') { + if (code === 'DDFSIT' || code === 'DOFSIT' || code === 'IDFSIT' || code === 'IOFSIT') { handleShowEditSitEntryDateModal(id, mtoShipmentID); } else { handleShowEditSitAddressModal(id, mtoShipmentID); diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.jsx index 15ccedb7d41..b69ecdad380 100644 --- a/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.jsx +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.jsx @@ -5,6 +5,8 @@ import PropTypes from 'prop-types'; import styles from './CreateShipmentServiceItemForm.module.scss'; import DestinationSITServiceItemForm from './DestinationSITServiceItemForm'; import OriginSITServiceItemForm from './OriginSITServiceItemForm'; +import InternationalDestinationSITServiceItemForm from './InternationalDestinationSITServiceItemForm'; +import InternationalOriginSITServiceItemForm from './InternationalOriginSITServiceItemForm'; import ShuttleSITServiceItemForm from './ShuttleSITServiceItemForm'; import DomesticCratingForm from './DomesticCratingForm'; import InternationalCratingForm from './InternationalCratingForm'; @@ -19,6 +21,8 @@ const CreateShipmentServiceItemForm = ({ shipment, createServiceItemMutation }) const { MTOServiceItemOriginSIT, MTOServiceItemDestSIT, + MTOServiceItemInternationalOriginSIT, + MTOServiceItemInternationalDestSIT, MTOServiceItemShuttle, MTOServiceItemDomesticCrating, MTOServiceItemInternationalCrating, @@ -47,6 +51,8 @@ const CreateShipmentServiceItemForm = ({ shipment, createServiceItemMutation }) <> + + {enableAlaskaFeatureFlag && } @@ -58,6 +64,14 @@ const CreateShipmentServiceItemForm = ({ shipment, createServiceItemMutation }) {selectedServiceItemType === MTOServiceItemDestSIT && ( )} + + {selectedServiceItemType === MTOServiceItemInternationalOriginSIT && ( + + )} + {selectedServiceItemType === MTOServiceItemInternationalDestSIT && ( + + )} + {selectedServiceItemType === MTOServiceItemShuttle && ( )} diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.jsx new file mode 100644 index 00000000000..14aab88084a --- /dev/null +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.jsx @@ -0,0 +1,133 @@ +import * as Yup from 'yup'; +import { Formik } from 'formik'; +import { Button } from '@trussworks/react-uswds'; +import React from 'react'; +import PropTypes from 'prop-types'; + +import { Form } from 'components/form/Form'; +import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextField'; +import { formatDateForSwagger } from 'shared/dates'; +import { formatAddressForPrimeAPI } from 'utils/formatters'; +import { DatePickerInput } from 'components/form/fields'; +import { ShipmentShape } from 'types/shipment'; +import TextField from 'components/form/fields/TextField/TextField'; +import Hint from 'components/Hint'; + +const destinationSITValidationSchema = Yup.object().shape({ + reason: Yup.string().required('Required'), + firstAvailableDeliveryDate1: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), + timeMilitary1: Yup.string().matches(/^(\d{4}Z)$/, 'Must be a valid military time (e.g. 1400Z)'), + firstAvailableDeliveryDate2: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), + timeMilitary2: Yup.string().matches(/^(\d{4}Z)$/, 'Must be a valid military time (e.g. 1400Z)'), + sitEntryDate: Yup.date() + .typeError('Enter a complete date in DD MMM YYYY format (day, month, year).') + .required('Required'), + sitDepartureDate: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), +}); + +const InternationalDestinationSITServiceItemForm = ({ shipment, submission }) => { + const initialValues = { + moveTaskOrderID: shipment.moveTaskOrderID, + mtoShipmentID: shipment.id, + modelType: 'MTOServiceItemInternationalDestSIT', + reServiceCode: 'IDFSIT', + reason: '', + firstAvailableDeliveryDate1: '', + dateOfContact1: '', + timeMilitary1: '', + firstAvailableDeliveryDate2: '', + dateOfContact2: '', + timeMilitary2: '', + sitEntryDate: '', + sitDepartureDate: '', + sitDestinationFinalAddress: { streetAddress1: '', streetAddress2: '', city: '', state: '', postalCode: '' }, + }; + + const onSubmit = (values) => { + const { + firstAvailableDeliveryDate1, + firstAvailableDeliveryDate2, + sitEntryDate, + sitDepartureDate, + sitDestinationFinalAddress, + timeMilitary1, + timeMilitary2, + dateOfContact1, + dateOfContact2, + ...serviceItemValues + } = values; + const body = { + firstAvailableDeliveryDate1: formatDateForSwagger(firstAvailableDeliveryDate1), + firstAvailableDeliveryDate2: formatDateForSwagger(firstAvailableDeliveryDate2), + dateOfContact1: formatDateForSwagger(dateOfContact1), + dateOfContact2: formatDateForSwagger(dateOfContact2), + sitEntryDate: formatDateForSwagger(sitEntryDate), + sitDepartureDate: sitDepartureDate ? formatDateForSwagger(sitDepartureDate) : null, + sitDestinationFinalAddress: sitDestinationFinalAddress.streetAddress1 + ? formatAddressForPrimeAPI(sitDestinationFinalAddress) + : null, + timeMilitary1: timeMilitary1 || null, + timeMilitary2: timeMilitary2 || null, + ...serviceItemValues, + }; + submission({ body }); + }; + + return ( + +
+ + + + + + + + + + + + + + + The following service items will be created:
+ DDFSIT (Destination 1st day SIT)
+ DDASIT (Destination additional days SIT)
+ DDDSIT (Destination SIT delivery)
+ DDSFSC (Destination SIT fuel surcharge)
+
+ NOTE: The above service items will use the current delivery address of the shipment as their + final delivery address. Ensure the shipment address is accurate before creating these service items. +
+ + +
+ ); +}; + +InternationalDestinationSITServiceItemForm.propTypes = { + shipment: ShipmentShape.isRequired, + submission: PropTypes.func.isRequired, +}; + +export default InternationalDestinationSITServiceItemForm; diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.jsx new file mode 100644 index 00000000000..dc0a094914f --- /dev/null +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.jsx @@ -0,0 +1,110 @@ +import * as Yup from 'yup'; +import { Formik } from 'formik'; +import { Button } from '@trussworks/react-uswds'; +import React from 'react'; +import { useNavigate, useParams, generatePath } from 'react-router-dom'; +import PropTypes from 'prop-types'; + +import { requiredAddressSchema, ZIP_CODE_REGEX } from 'utils/validation'; +import { formatDateForSwagger } from 'shared/dates'; +import { formatAddressForPrimeAPI } from 'utils/formatters'; +import { Form } from 'components/form/Form'; +import TextField from 'components/form/fields/TextField/TextField'; +import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextField'; +import { DatePickerInput } from 'components/form/fields'; +import { AddressFields } from 'components/form/AddressFields/AddressFields'; +import { ShipmentShape } from 'types/shipment'; +import { primeSimulatorRoutes } from 'constants/routes'; + +const originSITValidationSchema = Yup.object().shape({ + reason: Yup.string().required('Required'), + sitPostalCode: Yup.string().matches(ZIP_CODE_REGEX, 'Must be valid zip code').required('Required'), + sitEntryDate: Yup.date() + .typeError('Enter a complete date in DD MMM YYYY format (day, month, year).') + .required('Required'), + sitDepartureDate: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), + sitHHGActualOrigin: requiredAddressSchema, +}); + +const InternationalOriginSITServiceItemForm = ({ shipment, submission }) => { + const initialValues = { + moveTaskOrderID: shipment.moveTaskOrderID, + mtoShipmentID: shipment.id, + modelType: 'MTOServiceItemInternationalOriginSIT', + reServiceCode: 'IOFSIT', + reason: '', + sitPostalCode: '', + sitEntryDate: '', + sitDepartureDate: '', // The Prime API is currently ignoring origin SIT departure date on creation + sitHHGActualOrigin: { + streetAddress1: '', + streetAddress2: '', + streetAddress3: '', + city: '', + state: '', + postalCode: '', + county: '', + }, + }; + + const onSubmit = (values) => { + const { sitEntryDate, sitDepartureDate, sitHHGActualOrigin, ...serviceItemValues } = values; + const body = { + sitEntryDate: formatDateForSwagger(sitEntryDate), + sitDepartureDate: sitDepartureDate ? formatDateForSwagger(sitDepartureDate) : null, + sitHHGActualOrigin: sitHHGActualOrigin.streetAddress1 ? formatAddressForPrimeAPI(sitHHGActualOrigin) : null, + ...serviceItemValues, + }; + submission({ body }); + }; + + const { moveCodeOrID } = useParams(); + const navigate = useNavigate(); + const handleCancel = () => { + navigate(generatePath(primeSimulatorRoutes.VIEW_MOVE_PATH, { moveCodeOrID })); + }; + + return ( + + {({ isValid, isSubmitting, handleSubmit, ...formikProps }) => { + return ( +
+ + + + + + + + + + + + + ); + }} +
+ ); +}; + +InternationalOriginSITServiceItemForm.propTypes = { + shipment: ShipmentShape.isRequired, + submission: PropTypes.func.isRequired, +}; + +export default InternationalOriginSITServiceItemForm; diff --git a/src/constants/prime.js b/src/constants/prime.js index 098dfe54dc7..7dbb5998b23 100644 --- a/src/constants/prime.js +++ b/src/constants/prime.js @@ -4,6 +4,8 @@ import { serviceItemCodes } from 'content/serviceItems'; export const createServiceItemModelTypes = { MTOServiceItemOriginSIT: 'MTOServiceItemOriginSIT', MTOServiceItemDestSIT: 'MTOServiceItemDestSIT', + MTOServiceItemInternationalOriginSIT: 'MTOServiceItemInternationalOriginSIT', + MTOServiceItemInternationalDestSIT: 'MTOServiceItemInternationalDestSIT', MTOServiceItemShuttle: 'MTOServiceItemShuttle', MTOServiceItemDomesticCrating: 'MTOServiceItemDomesticCrating', MTOServiceItemInternationalCrating: 'MTOServiceItemInternationalCrating', diff --git a/src/constants/serviceItems.js b/src/constants/serviceItems.js index 01dad1c3eb0..fc23a504bb4 100644 --- a/src/constants/serviceItems.js +++ b/src/constants/serviceItems.js @@ -150,6 +150,14 @@ const SERVICE_ITEM_CODES = { IHPK: 'IHPK', IHUPK: 'IHUPK', ISLH: 'ISLH', + IDDSIT: 'IDDSIT', + IDASIT: 'IDASIT', + IOASIT: 'IOASIT', + IOFSIT: 'IOFSIT', + IOPSIT: 'IOPSIT', + IDFSIT: 'IDFSIT', + IOSFSC: 'IOSFSC', + IDSFSC: 'IDSFSC', }; const SERVICE_ITEMS_ALLOWED_WEIGHT_BILLED_PARAM = [ @@ -177,6 +185,15 @@ const SIT_SERVICE_ITEMS_ALLOWED_UPDATE = [ SERVICE_ITEM_CODES.DDFSIT, SERVICE_ITEM_CODES.DOSFSC, SERVICE_ITEM_CODES.DDSFSC, + + SERVICE_ITEM_CODES.IDDSIT, + SERVICE_ITEM_CODES.IDASIT, + SERVICE_ITEM_CODES.IOASIT, + SERVICE_ITEM_CODES.IOFSIT, + SERVICE_ITEM_CODES.IOPSIT, + SERVICE_ITEM_CODES.IDFSIT, + SERVICE_ITEM_CODES.IOSFSC, + SERVICE_ITEM_CODES.IDSFSC, ]; /** @@ -195,6 +212,18 @@ const SIT_SERVICE_ITEM_CODES = { DDASIT: 'DDASIT', /** Domestic destination SIT delivery */ DDDSIT: 'DDDSIT', + /** International origin 1st day SIT */ + IOFSIT: 'IOFSIT', + /** International origin Additional day SIT */ + IOASIT: 'IOASIT', + /** International origin SIT pickup */ + IOPSIT: 'IOPSIT', + /** International destination 1st day SIT */ + IDFSIT: 'IDFSIT', + /** International destination Additional day SIT */ + IDASIT: 'IDASIT', + /** International destination SIT delivery */ + IDDSIT: 'IDDSIT', }; // TODO - temporary, will remove once all service item calculations are implemented diff --git a/src/constants/sitUpdates.js b/src/constants/sitUpdates.js index 6a724b5e205..7e7bedb2fc2 100644 --- a/src/constants/sitUpdates.js +++ b/src/constants/sitUpdates.js @@ -1,6 +1,6 @@ // allowing edit of SIT entry date for Domestic destination 1st day SIT (DDFSIT) // allowing edit of SIT entry date for Domestic origin 1st day SIT (DOFSIT) -export const ALLOWED_SIT_UPDATE_SI_CODES = ['DOFSIT', 'DDFSIT']; +export const ALLOWED_SIT_UPDATE_SI_CODES = ['DOFSIT', 'DDFSIT', 'IOFSIT', 'IDFSIT']; // allowing display of old service item details for following SIT types which can be resubmitted export const ALLOWED_RESUBMISSION_SI_CODES = [ @@ -12,4 +12,12 @@ export const ALLOWED_RESUBMISSION_SI_CODES = [ 'DOSFSC', 'DDASIT', 'DDSFSC', + 'IDFSIT', + 'IDASIT', + 'IDDSIT', + 'IDSFSC', + 'IOFSIT', + 'IOASIT', + 'IOPSIT', + 'IOSFSC', ]; diff --git a/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalDestSITForm.jsx b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalDestSITForm.jsx new file mode 100644 index 00000000000..4de27935ee3 --- /dev/null +++ b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalDestSITForm.jsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { Formik } from 'formik'; +import { useNavigate, useParams, generatePath } from 'react-router-dom'; +import { FormGroup } from '@trussworks/react-uswds'; +import classnames from 'classnames'; + +import styles from './PrimeUIUpdateSITForms.module.scss'; + +import SectionWrapper from 'components/Customer/SectionWrapper'; +import formStyles from 'styles/form.module.scss'; +import { Form } from 'components/form/Form'; +import TextField from 'components/form/fields/TextField/TextField'; +import WizardNavigation from 'components/Customer/WizardNavigation/WizardNavigation'; +import descriptionListStyles from 'styles/descriptionList.module.scss'; +import { primeSimulatorRoutes } from 'constants/routes'; +import { DatePickerInput } from 'components/form/fields'; +import { SERVICE_ITEM_STATUSES } from 'constants/serviceItems'; + +const PrimeUIUpdateInternationalDestSITForm = ({ initialValues, onSubmit, serviceItem }) => { + const { moveCodeOrID } = useParams(); + const navigate = useNavigate(); + + const handleClose = () => { + navigate(generatePath(primeSimulatorRoutes.VIEW_MOVE_PATH, { moveCodeOrID })); + }; + + return ( + + {({ handleSubmit }) => ( +
+ +
+

Update International Destination SIT Service Item

+ +
+ Here you can update specific fields for an international destination SIT service item.
+ At this time, only the following values can be updated:
{' '} + + SIT Departure Date
+ SIT Requested Delivery
+ SIT Customer Contacted
+ Update Reason +
+
+
+
+ +

+ {serviceItem.reServiceCode} - {serviceItem.reServiceName} +

+
+
+
ID:
+
{serviceItem.id}
+
+
+
MTO ID:
+
{serviceItem.moveTaskOrderID}
+
+
+
Shipment ID:
+
{serviceItem.mtoShipmentID}
+
+
+
Status:
+
{serviceItem.status}
+
+
+
+ + + +
+ {serviceItem.status === SERVICE_ITEM_STATUSES.REJECTED && ( + + )} +
+ +
+
+
+ )} +
+ ); +}; + +export default PrimeUIUpdateInternationalDestSITForm; diff --git a/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalOriginSITForm.jsx b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalOriginSITForm.jsx new file mode 100644 index 00000000000..eead130eff2 --- /dev/null +++ b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalOriginSITForm.jsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { Formik } from 'formik'; +import { useNavigate, useParams, generatePath } from 'react-router-dom'; +import { FormGroup } from '@trussworks/react-uswds'; +import classnames from 'classnames'; + +import styles from './PrimeUIUpdateSITForms.module.scss'; + +import SectionWrapper from 'components/Customer/SectionWrapper'; +import formStyles from 'styles/form.module.scss'; +import { Form } from 'components/form/Form'; +import TextField from 'components/form/fields/TextField/TextField'; +import WizardNavigation from 'components/Customer/WizardNavigation/WizardNavigation'; +import descriptionListStyles from 'styles/descriptionList.module.scss'; +import { primeSimulatorRoutes } from 'constants/routes'; +import { DatePickerInput } from 'components/form/fields'; +import { SERVICE_ITEM_STATUSES } from 'constants/serviceItems'; + +const PrimeUIUpdateInternationalOriginSITForm = ({ initialValues, onSubmit, serviceItem }) => { + const { moveCodeOrID } = useParams(); + const navigate = useNavigate(); + + const handleClose = () => { + navigate(generatePath(primeSimulatorRoutes.VIEW_MOVE_PATH, { moveCodeOrID })); + }; + + return ( + + {({ handleSubmit }) => ( +
+ +
+

Update International Origin SIT Service Item

+ +
+ Here you can update specific fields for an origin SIT service item.
+ At this time, only the following values can be updated:
{' '} + + SIT Departure Date
+ SIT Requested Delivery
+ SIT Customer Contacted
+ Update Reason +
+
+
+
+ +

+ {serviceItem.reServiceCode} - {serviceItem.reServiceName} +

+
+
+
ID:
+
{serviceItem.id}
+
+
+
MTO ID:
+
{serviceItem.moveTaskOrderID}
+
+
+
Shipment ID:
+
{serviceItem.mtoShipmentID}
+
+
+
Status:
+
{serviceItem.status}
+
+
+
+ + + +
+ {serviceItem.status === SERVICE_ITEM_STATUSES.REJECTED && ( + + )} +
+ +
+
+
+ )} +
+ ); +}; + +export default PrimeUIUpdateInternationalOriginSITForm; diff --git a/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateSitServiceItem.jsx b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateSitServiceItem.jsx index 0a39bc1e6a3..86704af666c 100644 --- a/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateSitServiceItem.jsx +++ b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateSitServiceItem.jsx @@ -6,6 +6,8 @@ import { connect } from 'react-redux'; import PrimeUIUpdateOriginSITForm from './PrimeUIUpdateOriginSITForm'; import PrimeUIUpdateDestSITForm from './PrimeUIUpdateDestSITForm'; +import PrimeUIUpdateInternationalOriginSITForm from './PrimeUIUpdateInternationalOriginSITForm'; +import PrimeUIUpdateInternationalDestSITForm from './PrimeUIUpdateInternationalDestSITForm'; import { updateMTOServiceItem } from 'services/primeApi'; import scrollToTop from 'shared/scrollToTop'; @@ -136,6 +138,21 @@ const PrimeUIUpdateSitServiceItem = ({ setFlashMessage }) => { onSubmit={onSubmit} /> ) : null} + {modelType === 'MTOServiceItemInternationalDestSIT' ? ( + + ) : null} + {modelType === 'MTOServiceItemInternationalOriginSIT' ? ( + + ) : null} diff --git a/swagger-def/definitions/prime/MTOServiceItemInternationalDestSIT.yaml b/swagger-def/definitions/prime/MTOServiceItemInternationalDestSIT.yaml new file mode 100644 index 00000000000..c356798e521 --- /dev/null +++ b/swagger-def/definitions/prime/MTOServiceItemInternationalDestSIT.yaml @@ -0,0 +1,74 @@ +description: Describes a international destination SIT service item. Subtype of a MTOServiceItem. +allOf: + - $ref: 'MTOServiceItem.yaml' + - type: object + properties: + reServiceCode: + type: string + description: Service code allowed for this model type. + enum: + - IDFSIT # International Destination First Day SIT + - IDASIT # International Destination Additional SIT + dateOfContact1: + format: date + type: string + description: Date of attempted contact by the prime corresponding to `timeMilitary1`. + x-nullable: true + dateOfContact2: + format: date + type: string + description: Date of attempted contact by the prime corresponding to `timeMilitary2`. + x-nullable: true + timeMilitary1: + type: string + example: 1400Z + description: Time of attempted contact corresponding to `dateOfContact1`, in military format. + pattern: '\d{4}Z' + x-nullable: true + timeMilitary2: + type: string + example: 1400Z + description: Time of attempted contact corresponding to `dateOfContact2`, in military format. + pattern: '\d{4}Z' + x-nullable: true + firstAvailableDeliveryDate1: + format: date + type: string + description: First available date that Prime can deliver SIT service item. + x-nullable: true + firstAvailableDeliveryDate2: + format: date + type: string + description: Second available date that Prime can deliver SIT service item. + x-nullable: true + sitEntryDate: + format: date + type: string + description: Entry date for the SIT + sitDepartureDate: + format: date + type: string + description: Departure date for SIT. This is the end date of the SIT at either origin or destination. This is optional as it can be updated using the UpdateMTOServiceItemSIT modelType at a later date. + x-nullable: true + sitDestinationFinalAddress: + $ref: '../Address.yaml' + reason: + type: string + description: > + The reason item has been placed in SIT. + x-nullable: true + x-omitempty: false + sitRequestedDelivery: + format: date + type: string + description: Date when the customer has requested delivery out of SIT. + x-nullable: true + sitCustomerContacted: + format: date + type: string + description: Date when the customer contacted the prime for a delivery out of SIT. + x-nullable: true + required: + - reServiceCode + - sitEntryDate + - reason diff --git a/swagger-def/definitions/prime/MTOServiceItemInternationalOriginSIT.yaml b/swagger-def/definitions/prime/MTOServiceItemInternationalOriginSIT.yaml new file mode 100644 index 00000000000..26843f1c6ec --- /dev/null +++ b/swagger-def/definitions/prime/MTOServiceItemInternationalOriginSIT.yaml @@ -0,0 +1,50 @@ +description: Describes a international origin SIT service item. Subtype of a MTOServiceItem. +allOf: + - $ref: 'MTOServiceItem.yaml' + - type: object + properties: + reServiceCode: + type: string + description: Service code allowed for this model type. + enum: + - IOFSIT # International Origin First Day SIT + - IOASIT # International Origin Additional SIT + reason: + type: string + example: Storage items need to be picked up + description: Explanation of why Prime is picking up SIT item. + sitPostalCode: + type: string + format: zip + example: '90210' + pattern: '^(\d{5}([\-]\d{4})?)$' + sitEntryDate: + format: date + type: string + description: Entry date for the SIT + sitDepartureDate: + format: date + type: string + x-nullable: true + description: Departure date for SIT. This is the end date of the SIT at either origin or destination. This is optional as it can be updated using the UpdateMTOServiceItemSIT modelType at a later date. + sitHHGActualOrigin: + $ref: '../Address.yaml' + sitHHGOriginalOrigin: + $ref: '../Address.yaml' + requestApprovalsRequestedStatus: + type: boolean + sitRequestedDelivery: + format: date + type: string + description: Date when the customer has requested delivery out of SIT. + x-nullable: true + sitCustomerContacted: + format: date + type: string + description: Date when the customer contacted the prime for a delivery out of SIT. + x-nullable: true + required: + - reServiceCode + - reason + - sitPostalCode + - sitEntryDate diff --git a/swagger-def/definitions/prime/MTOServiceItemModelType.yaml b/swagger-def/definitions/prime/MTOServiceItemModelType.yaml index f96badf678a..96c05c0547c 100644 --- a/swagger-def/definitions/prime/MTOServiceItemModelType.yaml +++ b/swagger-def/definitions/prime/MTOServiceItemModelType.yaml @@ -6,6 +6,8 @@ description: > corresponding to the service item type. * DOFSIT, DOASIT - MTOServiceItemOriginSIT * DDFSIT, DDASIT - MTOServiceItemDestSIT + * IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT + * IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT * DOSHUT, DDSHUT - MTOServiceItemShuttle * DCRT, DUCRT - MTOServiceItemDomesticCrating * ICRT, IUCRT - MTOServiceItemInternationalCrating @@ -17,6 +19,8 @@ enum: - MTOServiceItemBasic - MTOServiceItemOriginSIT - MTOServiceItemDestSIT + - MTOServiceItemInternationalOriginSIT + - MTOServiceItemInternationalDestSIT - MTOServiceItemShuttle - MTOServiceItemDomesticCrating - MTOServiceItemInternationalCrating diff --git a/swagger-def/prime.yaml b/swagger-def/prime.yaml index f34788446eb..0ee109cfb62 100644 --- a/swagger-def/prime.yaml +++ b/swagger-def/prime.yaml @@ -1683,12 +1683,16 @@ definitions: $ref: 'definitions/prime/MTOServiceItemBasic.yaml' MTOServiceItemDestSIT: # spectral oas2-unused-definition is OK here due to polymorphism $ref: 'definitions/prime/MTOServiceItemDestSIT.yaml' + MTOServiceItemInternationalDestSIT: # spectral oas2-unused-definition is OK here due to polymorphism + $ref: 'definitions/prime/MTOServiceItemInternationalDestSIT.yaml' MTOServiceItemDomesticCrating: # spectral oas2-unused-definition is OK here due to polymorphism $ref: 'definitions/prime/MTOServiceItemDomesticCrating.yaml' MTOServiceItemInternationalCrating: # spectral oas2-unused-definition is OK here due to polymorphism $ref: 'definitions/prime/MTOServiceItemInternationalCrating.yaml' MTOServiceItemOriginSIT: # spectral oas2-unused-definition is OK here due to polymorphism $ref: 'definitions/prime/MTOServiceItemOriginSIT.yaml' + MTOServiceItemInternationalOriginSIT: # spectral oas2-unused-definition is OK here due to polymorphism + $ref: 'definitions/prime/MTOServiceItemInternationalOriginSIT.yaml' MTOServiceItemShuttle: # spectral oas2-unused-definition is OK here due to polymorphism $ref: 'definitions/prime/MTOServiceItemShuttle.yaml' MTOServiceItemInternationalFuelSurcharge: # spectral oas2-unused-definition is OK here due to polymorphism @@ -1773,6 +1777,14 @@ definitions: * DOFSIT - UpdateMTOServiceItemSIT * DOSFSC - UpdateMTOServiceItemSIT * DDSFSC - UpdateMTOServiceItemSIT + * IDDSIT - UpdateMTOServiceItemSIT + * IDFSIT - UpdateMTOServiceItemSIT + * IDASIT - UpdateMTOServiceItemSIT + * IOPSIT - UpdateMTOServiceItemSIT + * IOASIT - UpdateMTOServiceItemSIT + * IOFSIT - UpdateMTOServiceItemSIT + * IOSFSC - UpdateMTOServiceItemSIT + * IDSFSC - UpdateMTOServiceItemSIT * DDSHUT - UpdateMTOServiceItemShuttle * DOSHUT - UpdateMTOServiceItemShuttle * PODFSC - UpdateMTOServiceItemInternationalPortFSC @@ -1828,6 +1840,14 @@ definitions: - DOASIT # Domestic Origin Add'l Days SIT - DOFSIT # Domestic Origin 1st Day SIT - DOSFSC # Domestic Origin Fuel Surcharge + - IDDSIT # International Destination SIT Delivery + - IDASIT # International Destination Add'l Days SIT + - IDFSIT # International Destination 1st Day SIT + - IDSFSC # International Destination Fuel Surcharge + - IOPSIT # International Origin SIT Pickup + - IOASIT # International Origin Add'l Days SIT + - IOFSIT # International Origin 1st Day SIT + - IOSFSC # International Origin Fuel Surcharge sitDepartureDate: format: date type: string diff --git a/swagger/prime.yaml b/swagger/prime.yaml index bcd51fed89d..072df16fe6a 100644 --- a/swagger/prime.yaml +++ b/swagger/prime.yaml @@ -2269,6 +2269,96 @@ definitions: - reServiceCode - sitEntryDate - reason + MTOServiceItemInternationalDestSIT: + description: >- + Describes a international destination SIT service item. Subtype of a + MTOServiceItem. + allOf: + - $ref: '#/definitions/MTOServiceItem' + - type: object + properties: + reServiceCode: + type: string + description: Service code allowed for this model type. + enum: + - IDFSIT + - IDASIT + dateOfContact1: + format: date + type: string + description: >- + Date of attempted contact by the prime corresponding to + `timeMilitary1`. + x-nullable: true + dateOfContact2: + format: date + type: string + description: >- + Date of attempted contact by the prime corresponding to + `timeMilitary2`. + x-nullable: true + timeMilitary1: + type: string + example: 1400Z + description: >- + Time of attempted contact corresponding to `dateOfContact1`, in + military format. + pattern: \d{4}Z + x-nullable: true + timeMilitary2: + type: string + example: 1400Z + description: >- + Time of attempted contact corresponding to `dateOfContact2`, in + military format. + pattern: \d{4}Z + x-nullable: true + firstAvailableDeliveryDate1: + format: date + type: string + description: First available date that Prime can deliver SIT service item. + x-nullable: true + firstAvailableDeliveryDate2: + format: date + type: string + description: Second available date that Prime can deliver SIT service item. + x-nullable: true + sitEntryDate: + format: date + type: string + description: Entry date for the SIT + sitDepartureDate: + format: date + type: string + description: >- + Departure date for SIT. This is the end date of the SIT at either + origin or destination. This is optional as it can be updated using + the UpdateMTOServiceItemSIT modelType at a later date. + x-nullable: true + sitDestinationFinalAddress: + $ref: '#/definitions/Address' + reason: + type: string + description: | + The reason item has been placed in SIT. + x-nullable: true + x-omitempty: false + sitRequestedDelivery: + format: date + type: string + description: Date when the customer has requested delivery out of SIT. + x-nullable: true + sitCustomerContacted: + format: date + type: string + description: >- + Date when the customer contacted the prime for a delivery out of + SIT. + x-nullable: true + required: + - reServiceCode + - sitEntryDate + - reason MTOServiceItemDomesticCrating: description: >- Describes a domestic crating/uncrating service item subtype of a @@ -2427,6 +2517,64 @@ definitions: - reason - sitPostalCode - sitEntryDate + MTOServiceItemInternationalOriginSIT: + description: >- + Describes a international origin SIT service item. Subtype of a + MTOServiceItem. + allOf: + - $ref: '#/definitions/MTOServiceItem' + - type: object + properties: + reServiceCode: + type: string + description: Service code allowed for this model type. + enum: + - IOFSIT + - IOASIT + reason: + type: string + example: Storage items need to be picked up + description: Explanation of why Prime is picking up SIT item. + sitPostalCode: + type: string + format: zip + example: '90210' + pattern: ^(\d{5}([\-]\d{4})?)$ + sitEntryDate: + format: date + type: string + description: Entry date for the SIT + sitDepartureDate: + format: date + type: string + x-nullable: true + description: >- + Departure date for SIT. This is the end date of the SIT at either + origin or destination. This is optional as it can be updated using + the UpdateMTOServiceItemSIT modelType at a later date. + sitHHGActualOrigin: + $ref: '#/definitions/Address' + sitHHGOriginalOrigin: + $ref: '#/definitions/Address' + requestApprovalsRequestedStatus: + type: boolean + sitRequestedDelivery: + format: date + type: string + description: Date when the customer has requested delivery out of SIT. + x-nullable: true + sitCustomerContacted: + format: date + type: string + description: >- + Date when the customer contacted the prime for a delivery out of + SIT. + x-nullable: true + required: + - reServiceCode + - reason + - sitPostalCode + - sitEntryDate MTOServiceItemShuttle: description: Describes a shuttle service item. allOf: @@ -2577,6 +2725,14 @@ definitions: * DOFSIT - UpdateMTOServiceItemSIT * DOSFSC - UpdateMTOServiceItemSIT * DDSFSC - UpdateMTOServiceItemSIT + * IDDSIT - UpdateMTOServiceItemSIT + * IDFSIT - UpdateMTOServiceItemSIT + * IDASIT - UpdateMTOServiceItemSIT + * IOPSIT - UpdateMTOServiceItemSIT + * IOASIT - UpdateMTOServiceItemSIT + * IOFSIT - UpdateMTOServiceItemSIT + * IOSFSC - UpdateMTOServiceItemSIT + * IDSFSC - UpdateMTOServiceItemSIT * DDSHUT - UpdateMTOServiceItemShuttle * DOSHUT - UpdateMTOServiceItemShuttle * PODFSC - UpdateMTOServiceItemInternationalPortFSC @@ -2640,6 +2796,14 @@ definitions: - DOASIT - DOFSIT - DOSFSC + - IDDSIT + - IDASIT + - IDFSIT + - IDSFSC + - IOPSIT + - IOASIT + - IOFSIT + - IOSFSC sitDepartureDate: format: date type: string @@ -3651,6 +3815,8 @@ definitions: corresponding to the service item type. * DOFSIT, DOASIT - MTOServiceItemOriginSIT * DDFSIT, DDASIT - MTOServiceItemDestSIT + * IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT + * IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT * DOSHUT, DDSHUT - MTOServiceItemShuttle * DCRT, DUCRT - MTOServiceItemDomesticCrating * ICRT, IUCRT - MTOServiceItemInternationalCrating @@ -3662,6 +3828,8 @@ definitions: - MTOServiceItemBasic - MTOServiceItemOriginSIT - MTOServiceItemDestSIT + - MTOServiceItemInternationalOriginSIT + - MTOServiceItemInternationalDestSIT - MTOServiceItemShuttle - MTOServiceItemDomesticCrating - MTOServiceItemInternationalCrating diff --git a/swagger/prime_v2.yaml b/swagger/prime_v2.yaml index 53b2599b343..271d94ee262 100644 --- a/swagger/prime_v2.yaml +++ b/swagger/prime_v2.yaml @@ -2067,6 +2067,8 @@ definitions: corresponding to the service item type. * DOFSIT, DOASIT - MTOServiceItemOriginSIT * DDFSIT, DDASIT - MTOServiceItemDestSIT + * IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT + * IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT * DOSHUT, DDSHUT - MTOServiceItemShuttle * DCRT, DUCRT - MTOServiceItemDomesticCrating * ICRT, IUCRT - MTOServiceItemInternationalCrating @@ -2078,6 +2080,8 @@ definitions: - MTOServiceItemBasic - MTOServiceItemOriginSIT - MTOServiceItemDestSIT + - MTOServiceItemInternationalOriginSIT + - MTOServiceItemInternationalDestSIT - MTOServiceItemShuttle - MTOServiceItemDomesticCrating - MTOServiceItemInternationalCrating diff --git a/swagger/prime_v3.yaml b/swagger/prime_v3.yaml index 21a25cbfa99..1add954fb3c 100644 --- a/swagger/prime_v3.yaml +++ b/swagger/prime_v3.yaml @@ -2155,6 +2155,8 @@ definitions: corresponding to the service item type. * DOFSIT, DOASIT - MTOServiceItemOriginSIT * DDFSIT, DDASIT - MTOServiceItemDestSIT + * IOFSIT, IOASIT - MTOServiceItemInternationalOriginSIT + * IDFSIT, IDASIT - MTOServiceItemInternationalDestSIT * DOSHUT, DDSHUT - MTOServiceItemShuttle * DCRT, DUCRT - MTOServiceItemDomesticCrating * ICRT, IUCRT - MTOServiceItemInternationalCrating @@ -2166,6 +2168,8 @@ definitions: - MTOServiceItemBasic - MTOServiceItemOriginSIT - MTOServiceItemDestSIT + - MTOServiceItemInternationalOriginSIT + - MTOServiceItemInternationalDestSIT - MTOServiceItemShuttle - MTOServiceItemDomesticCrating - MTOServiceItemInternationalCrating From 22c7cdee805e4eb9517479baf1366a9d83e25931 Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Thu, 23 Jan 2025 20:39:14 +0000 Subject: [PATCH 02/26] merge 21410 merge and deleted duplicate international Org/Dest create SIT forms --- pkg/models/mto_shipments.go | 2 + .../CreateShipmentServiceItemForm.jsx | 26 +++- .../DestinationSITServiceItemForm.jsx | 27 +++- ...rnationalDestinationSITServiceItemForm.jsx | 133 ------------------ .../InternationalOriginSITServiceItemForm.jsx | 110 --------------- .../OriginSITServiceItemForm.jsx | 7 +- 6 files changed, 46 insertions(+), 259 deletions(-) delete mode 100644 src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.jsx delete mode 100644 src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.jsx diff --git a/pkg/models/mto_shipments.go b/pkg/models/mto_shipments.go index 5fff5f66adc..eff540210ef 100644 --- a/pkg/models/mto_shipments.go +++ b/pkg/models/mto_shipments.go @@ -48,6 +48,8 @@ var internationalAccessorialServiceItems = []ReServiceCode{ ReServiceCodeIDDSIT, ReServiceCodeIDSHUT, ReServiceCodeIOSHUT, + ReServiceCodeIOSFSC, + ReServiceCodeIDSFSC, } const ( diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.jsx index ea83899c299..ba4008d6675 100644 --- a/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.jsx +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.jsx @@ -5,8 +5,6 @@ import PropTypes from 'prop-types'; import styles from './CreateShipmentServiceItemForm.module.scss'; import DestinationSITServiceItemForm from './DestinationSITServiceItemForm'; import OriginSITServiceItemForm from './OriginSITServiceItemForm'; -import InternationalDestinationSITServiceItemForm from './InternationalDestinationSITServiceItemForm'; -import InternationalOriginSITServiceItemForm from './InternationalOriginSITServiceItemForm'; import ShuttleSITServiceItemForm from './ShuttleSITServiceItemForm'; import DomesticCratingForm from './DomesticCratingForm'; import InternationalCratingForm from './InternationalCratingForm'; @@ -62,17 +60,33 @@ const CreateShipmentServiceItemForm = ({ shipment, createServiceItemMutation }) {selectedServiceItemType === MTOServiceItemOriginSIT && ( - + )} {selectedServiceItemType === MTOServiceItemDestSIT && ( - + )} {selectedServiceItemType === MTOServiceItemInternationalOriginSIT && ( - + )} {selectedServiceItemType === MTOServiceItemInternationalDestSIT && ( - + )} {selectedServiceItemType === MTOServiceItemShuttle && ( diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/DestinationSITServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/DestinationSITServiceItemForm.jsx index 0eef9278cca..497d442205e 100644 --- a/src/components/PrimeUI/CreateShipmentServiceItemForm/DestinationSITServiceItemForm.jsx +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/DestinationSITServiceItemForm.jsx @@ -25,12 +25,12 @@ const destinationSITValidationSchema = Yup.object().shape({ sitDepartureDate: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), }); -const DestinationSITServiceItemForm = ({ shipment, submission }) => { +const DestinationSITServiceItemForm = ({ shipment, submission, isDomestic }) => { const initialValues = { moveTaskOrderID: shipment.moveTaskOrderID, mtoShipmentID: shipment.id, - modelType: 'MTOServiceItemDestSIT', - reServiceCode: 'DDFSIT', + modelType: isDomestic ? 'MTOServiceItemDestSIT' : 'MTOServiceItemInternationalDestSIT', + reServiceCode: isDomestic ? 'DDFSIT' : 'IDFSIT', reason: '', firstAvailableDeliveryDate1: '', dateOfContact1: '', @@ -111,10 +111,22 @@ const DestinationSITServiceItemForm = ({ shipment, submission }) => { The following service items will be created:
- DDFSIT (Destination 1st day SIT)
- DDASIT (Destination additional days SIT)
- DDDSIT (Destination SIT delivery)
- DDSFSC (Destination SIT fuel surcharge)
+ {isDomestic && ( + <> + DDFSIT (Destination 1st day SIT)
+ DDASIT (Destination additional days SIT)
+ DDDSIT (Destination SIT delivery)
+ DDSFSC (Destination SIT fuel surcharge)
+ + )} + {!isDomestic && ( + <> + IDFSIT (Destination 1st day SIT)
+ IDASIT (Destination additional days SIT)
+ IDDSIT (Destination SIT delivery)
+ IDSFSC (Destination SIT fuel surcharge)
+ + )}
NOTE: The above service items will use the current delivery address of the shipment as their final delivery address. Ensure the shipment address is accurate before creating these service items. @@ -128,6 +140,7 @@ const DestinationSITServiceItemForm = ({ shipment, submission }) => { DestinationSITServiceItemForm.propTypes = { shipment: ShipmentShape.isRequired, submission: PropTypes.func.isRequired, + isDomestic: PropTypes.bool.isRequired, }; export default DestinationSITServiceItemForm; diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.jsx deleted file mode 100644 index 14aab88084a..00000000000 --- a/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.jsx +++ /dev/null @@ -1,133 +0,0 @@ -import * as Yup from 'yup'; -import { Formik } from 'formik'; -import { Button } from '@trussworks/react-uswds'; -import React from 'react'; -import PropTypes from 'prop-types'; - -import { Form } from 'components/form/Form'; -import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextField'; -import { formatDateForSwagger } from 'shared/dates'; -import { formatAddressForPrimeAPI } from 'utils/formatters'; -import { DatePickerInput } from 'components/form/fields'; -import { ShipmentShape } from 'types/shipment'; -import TextField from 'components/form/fields/TextField/TextField'; -import Hint from 'components/Hint'; - -const destinationSITValidationSchema = Yup.object().shape({ - reason: Yup.string().required('Required'), - firstAvailableDeliveryDate1: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), - timeMilitary1: Yup.string().matches(/^(\d{4}Z)$/, 'Must be a valid military time (e.g. 1400Z)'), - firstAvailableDeliveryDate2: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), - timeMilitary2: Yup.string().matches(/^(\d{4}Z)$/, 'Must be a valid military time (e.g. 1400Z)'), - sitEntryDate: Yup.date() - .typeError('Enter a complete date in DD MMM YYYY format (day, month, year).') - .required('Required'), - sitDepartureDate: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), -}); - -const InternationalDestinationSITServiceItemForm = ({ shipment, submission }) => { - const initialValues = { - moveTaskOrderID: shipment.moveTaskOrderID, - mtoShipmentID: shipment.id, - modelType: 'MTOServiceItemInternationalDestSIT', - reServiceCode: 'IDFSIT', - reason: '', - firstAvailableDeliveryDate1: '', - dateOfContact1: '', - timeMilitary1: '', - firstAvailableDeliveryDate2: '', - dateOfContact2: '', - timeMilitary2: '', - sitEntryDate: '', - sitDepartureDate: '', - sitDestinationFinalAddress: { streetAddress1: '', streetAddress2: '', city: '', state: '', postalCode: '' }, - }; - - const onSubmit = (values) => { - const { - firstAvailableDeliveryDate1, - firstAvailableDeliveryDate2, - sitEntryDate, - sitDepartureDate, - sitDestinationFinalAddress, - timeMilitary1, - timeMilitary2, - dateOfContact1, - dateOfContact2, - ...serviceItemValues - } = values; - const body = { - firstAvailableDeliveryDate1: formatDateForSwagger(firstAvailableDeliveryDate1), - firstAvailableDeliveryDate2: formatDateForSwagger(firstAvailableDeliveryDate2), - dateOfContact1: formatDateForSwagger(dateOfContact1), - dateOfContact2: formatDateForSwagger(dateOfContact2), - sitEntryDate: formatDateForSwagger(sitEntryDate), - sitDepartureDate: sitDepartureDate ? formatDateForSwagger(sitDepartureDate) : null, - sitDestinationFinalAddress: sitDestinationFinalAddress.streetAddress1 - ? formatAddressForPrimeAPI(sitDestinationFinalAddress) - : null, - timeMilitary1: timeMilitary1 || null, - timeMilitary2: timeMilitary2 || null, - ...serviceItemValues, - }; - submission({ body }); - }; - - return ( - -
- - - - - - - - - - - - - - - The following service items will be created:
- DDFSIT (Destination 1st day SIT)
- DDASIT (Destination additional days SIT)
- DDDSIT (Destination SIT delivery)
- DDSFSC (Destination SIT fuel surcharge)
-
- NOTE: The above service items will use the current delivery address of the shipment as their - final delivery address. Ensure the shipment address is accurate before creating these service items. -
- - -
- ); -}; - -InternationalDestinationSITServiceItemForm.propTypes = { - shipment: ShipmentShape.isRequired, - submission: PropTypes.func.isRequired, -}; - -export default InternationalDestinationSITServiceItemForm; diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.jsx deleted file mode 100644 index dc0a094914f..00000000000 --- a/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.jsx +++ /dev/null @@ -1,110 +0,0 @@ -import * as Yup from 'yup'; -import { Formik } from 'formik'; -import { Button } from '@trussworks/react-uswds'; -import React from 'react'; -import { useNavigate, useParams, generatePath } from 'react-router-dom'; -import PropTypes from 'prop-types'; - -import { requiredAddressSchema, ZIP_CODE_REGEX } from 'utils/validation'; -import { formatDateForSwagger } from 'shared/dates'; -import { formatAddressForPrimeAPI } from 'utils/formatters'; -import { Form } from 'components/form/Form'; -import TextField from 'components/form/fields/TextField/TextField'; -import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextField'; -import { DatePickerInput } from 'components/form/fields'; -import { AddressFields } from 'components/form/AddressFields/AddressFields'; -import { ShipmentShape } from 'types/shipment'; -import { primeSimulatorRoutes } from 'constants/routes'; - -const originSITValidationSchema = Yup.object().shape({ - reason: Yup.string().required('Required'), - sitPostalCode: Yup.string().matches(ZIP_CODE_REGEX, 'Must be valid zip code').required('Required'), - sitEntryDate: Yup.date() - .typeError('Enter a complete date in DD MMM YYYY format (day, month, year).') - .required('Required'), - sitDepartureDate: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), - sitHHGActualOrigin: requiredAddressSchema, -}); - -const InternationalOriginSITServiceItemForm = ({ shipment, submission }) => { - const initialValues = { - moveTaskOrderID: shipment.moveTaskOrderID, - mtoShipmentID: shipment.id, - modelType: 'MTOServiceItemInternationalOriginSIT', - reServiceCode: 'IOFSIT', - reason: '', - sitPostalCode: '', - sitEntryDate: '', - sitDepartureDate: '', // The Prime API is currently ignoring origin SIT departure date on creation - sitHHGActualOrigin: { - streetAddress1: '', - streetAddress2: '', - streetAddress3: '', - city: '', - state: '', - postalCode: '', - county: '', - }, - }; - - const onSubmit = (values) => { - const { sitEntryDate, sitDepartureDate, sitHHGActualOrigin, ...serviceItemValues } = values; - const body = { - sitEntryDate: formatDateForSwagger(sitEntryDate), - sitDepartureDate: sitDepartureDate ? formatDateForSwagger(sitDepartureDate) : null, - sitHHGActualOrigin: sitHHGActualOrigin.streetAddress1 ? formatAddressForPrimeAPI(sitHHGActualOrigin) : null, - ...serviceItemValues, - }; - submission({ body }); - }; - - const { moveCodeOrID } = useParams(); - const navigate = useNavigate(); - const handleCancel = () => { - navigate(generatePath(primeSimulatorRoutes.VIEW_MOVE_PATH, { moveCodeOrID })); - }; - - return ( - - {({ isValid, isSubmitting, handleSubmit, ...formikProps }) => { - return ( -
- - - - - - - - - - - - - ); - }} -
- ); -}; - -InternationalOriginSITServiceItemForm.propTypes = { - shipment: ShipmentShape.isRequired, - submission: PropTypes.func.isRequired, -}; - -export default InternationalOriginSITServiceItemForm; diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/OriginSITServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/OriginSITServiceItemForm.jsx index dc86b6ad5e1..f261bcbf372 100644 --- a/src/components/PrimeUI/CreateShipmentServiceItemForm/OriginSITServiceItemForm.jsx +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/OriginSITServiceItemForm.jsx @@ -26,12 +26,12 @@ const originSITValidationSchema = Yup.object().shape({ sitHHGActualOrigin: requiredAddressSchema, }); -const OriginSITServiceItemForm = ({ shipment, submission }) => { +const OriginSITServiceItemForm = ({ shipment, submission, isDomestic }) => { const initialValues = { moveTaskOrderID: shipment.moveTaskOrderID, mtoShipmentID: shipment.id, - modelType: 'MTOServiceItemOriginSIT', - reServiceCode: 'DOFSIT', + modelType: isDomestic ? 'MTOServiceItemOriginSIT' : 'MTOServiceItemInternationalOriginSIT', + reServiceCode: isDomestic ? 'DOFSIT' : 'IOFSIT', reason: '', sitPostalCode: '', sitEntryDate: '', @@ -105,6 +105,7 @@ const OriginSITServiceItemForm = ({ shipment, submission }) => { OriginSITServiceItemForm.propTypes = { shipment: ShipmentShape.isRequired, submission: PropTypes.func.isRequired, + isDomestic: PropTypes.bool.isRequired, }; export default OriginSITServiceItemForm; From bc29b353d4d37233dc87d77d62a00cbcae08a6fe Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Tue, 28 Jan 2025 17:13:49 +0000 Subject: [PATCH 03/26] update yaml+description for international SITs --- pkg/gen/primeapi/embedded_spec.go | 12 +- .../create_m_t_o_service_item.go | 75 +++++++++++ .../update_m_t_o_service_item.go | 8 ++ .../mto_service_item_client.go | 83 ++++++++++++ pkg/gen/primemessages/service_item.go | 2 + pkg/gen/primev2api/embedded_spec.go | 16 ++- .../update_m_t_o_service_item_model_type.go | 4 + .../update_m_t_o_service_item_s_i_t.go | 8 +- pkg/gen/primev3api/embedded_spec.go | 16 ++- .../update_m_t_o_service_item_model_type.go | 4 + .../update_m_t_o_service_item_s_i_t.go | 8 +- .../mto_service_item_creator.go | 15 ++- swagger-def/prime.yaml | 86 ++++++++++++ swagger-def/prime_v2.yaml | 8 ++ swagger-def/prime_v3.yaml | 8 ++ swagger/prime.yaml | 124 ++++++++++++++++++ swagger/prime_v2.yaml | 8 ++ swagger/prime_v3.yaml | 8 ++ 18 files changed, 465 insertions(+), 28 deletions(-) diff --git a/pkg/gen/primeapi/embedded_spec.go b/pkg/gen/primeapi/embedded_spec.go index c7d1ea4f5af..8c8aaa233bb 100644 --- a/pkg/gen/primeapi/embedded_spec.go +++ b/pkg/gen/primeapi/embedded_spec.go @@ -298,7 +298,7 @@ func init() { }, "/mto-service-items": { "post": { - "description": "Creates one or more MTOServiceItems. Not all service items may be created, please see details below.\n\nThis endpoint supports different body definitions. In the modelType field below, select the modelType corresponding\n to the service item you wish to create and the documentation will update with the new definition.\n\nUpon creation these items are associated with a Move Task Order and an MTO Shipment.\nThe request must include UUIDs for the MTO and MTO Shipment connected to this service item. Some service item types require\nadditional service items to be autogenerated when added - all created service items, autogenerated included,\nwill be returned in the response.\n\nTo update a service item, please use [updateMTOServiceItem](#operation/updateMTOServiceItem) endpoint.\n\n---\n\n**` + "`" + `MTOServiceItemOriginSIT` + "`" + `**\n\nMTOServiceItemOriginSIT is a subtype of MTOServiceItem.\n\nThis model type describes a domestic origin SIT service item. Items can be created using this\nmodel type with the following codes:\n\n**DOFSIT**\n\n**1st day origin SIT service item**. When a DOFSIT is requested, the API will auto-create the following group of service items:\n * DOFSIT - Domestic origin 1st day SIT\n * DOASIT - Domestic origin Additional day SIT\n * DOPSIT - Domestic origin SIT pickup\n * DOSFSC - Domestic origin SIT fuel surcharge\n\n**DOASIT**\n\n**Addt'l days origin SIT service item**. This represents an additional day of storage for the same item.\nAdditional DOASIT service items can be created and added to an existing shipment that **includes a DOFSIT service item**.\n\n---\n\n**` + "`" + `MTOServiceItemDestSIT` + "`" + `**\n\nMTOServiceItemDestSIT is a subtype of MTOServiceItem.\n\nThis model type describes a domestic destination SIT service item. Items can be created using this\nmodel type with the following codes:\n\n**DDFSIT**\n\n**1st day destination SIT service item**.\n\nThese additional fields are optional for creating a DDFSIT:\n * ` + "`" + `firstAvailableDeliveryDate1` + "`" + `\n * string \u003cdate\u003e\n * First available date that Prime can deliver SIT service item.\n * firstAvailableDeliveryDate1, dateOfContact1, and timeMilitary1 are required together\n * ` + "`" + `dateOfContact1` + "`" + `\n * string \u003cdate\u003e\n * Date of attempted contact by the prime corresponding to ` + "`" + `timeMilitary1` + "`" + `\n * dateOfContact1, timeMilitary1, and firstAvailableDeliveryDate1 are required together\n * ` + "`" + `timeMilitary1` + "`" + `\n * string\\d{4}Z\n * Time of attempted contact corresponding to ` + "`" + `dateOfContact1` + "`" + `, in military format.\n * timeMilitary1, dateOfContact1, and firstAvailableDeliveryDate1 are required together\n * ` + "`" + `firstAvailableDeliveryDate2` + "`" + `\n * string \u003cdate\u003e\n * Second available date that Prime can deliver SIT service item.\n * firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 are required together\n * ` + "`" + `dateOfContact2` + "`" + `\n * string \u003cdate\u003e\n * Date of attempted contact delivery by the prime corresponding to ` + "`" + `timeMilitary2` + "`" + `\n * dateOfContact2, timeMilitary2, and firstAvailableDeliveryDate2 are required together\n * ` + "`" + `timeMilitary2` + "`" + `\n * string\\d{4}Z\n * Time of attempted contact corresponding to ` + "`" + `dateOfContact2` + "`" + `, in military format.\n * timeMilitary2, dateOfContact2, and firstAvailableDeliveryDate2 are required together\n\nWhen a DDFSIT is requested, the API will auto-create the following group of service items:\n * DDFSIT - Domestic destination 1st day SIT\n * DDASIT - Domestic destination Additional day SIT\n * DDDSIT - Domestic destination SIT delivery\n * DDSFSC - Domestic destination SIT fuel surcharge\n\n**NOTE** When providing the ` + "`" + `sitEntryDate` + "`" + ` value in the payload, please ensure that the date is not BEFORE\n` + "`" + `firstAvailableDeliveryDate1` + "`" + ` or ` + "`" + `firstAvailableDeliveryDate2` + "`" + `. If it is, you will receive an error response.\n\n**DDASIT**\n\n**Addt'l days destination SIT service item**. This represents an additional day of storage for the same item.\nAdditional DDASIT service items can be created and added to an existing shipment that **includes a DDFSIT service item**.\n", + "description": "Creates one or more MTOServiceItems. Not all service items may be created, please see details below.\n\nThis endpoint supports different body definitions. In the modelType field below, select the modelType corresponding\n to the service item you wish to create and the documentation will update with the new definition.\n\nUpon creation these items are associated with a Move Task Order and an MTO Shipment.\nThe request must include UUIDs for the MTO and MTO Shipment connected to this service item. Some service item types require\nadditional service items to be autogenerated when added - all created service items, autogenerated included,\nwill be returned in the response.\n\nTo update a service item, please use [updateMTOServiceItem](#operation/updateMTOServiceItem) endpoint.\n\n---\n\n**` + "`" + `MTOServiceItemOriginSIT` + "`" + `**\n\nMTOServiceItemOriginSIT is a subtype of MTOServiceItem.\n\nThis model type describes a domestic origin SIT service item. Items can be created using this\nmodel type with the following codes:\n\n**DOFSIT**\n\n**1st day origin SIT service item**. When a DOFSIT is requested, the API will auto-create the following group of service items:\n * DOFSIT - Domestic origin 1st day SIT\n * DOASIT - Domestic origin Additional day SIT\n * DOPSIT - Domestic origin SIT pickup\n * DOSFSC - Domestic origin SIT fuel surcharge\n\n**DOASIT**\n\n**Addt'l days origin SIT service item**. This represents an additional day of storage for the same item.\nAdditional DOASIT service items can be created and added to an existing shipment that **includes a DOFSIT service item**.\n\n---\n\n**` + "`" + `MTOServiceItemDestSIT` + "`" + `**\n\nMTOServiceItemDestSIT is a subtype of MTOServiceItem.\n\nThis model type describes a domestic destination SIT service item. Items can be created using this\nmodel type with the following codes:\n\n**DDFSIT**\n\n**1st day destination SIT service item**.\n\nThese additional fields are optional for creating a DDFSIT:\n * ` + "`" + `firstAvailableDeliveryDate1` + "`" + `\n * string \u003cdate\u003e\n * First available date that Prime can deliver SIT service item.\n * firstAvailableDeliveryDate1, dateOfContact1, and timeMilitary1 are required together\n * ` + "`" + `dateOfContact1` + "`" + `\n * string \u003cdate\u003e\n * Date of attempted contact by the prime corresponding to ` + "`" + `timeMilitary1` + "`" + `\n * dateOfContact1, timeMilitary1, and firstAvailableDeliveryDate1 are required together\n * ` + "`" + `timeMilitary1` + "`" + `\n * string\\d{4}Z\n * Time of attempted contact corresponding to ` + "`" + `dateOfContact1` + "`" + `, in military format.\n * timeMilitary1, dateOfContact1, and firstAvailableDeliveryDate1 are required together\n * ` + "`" + `firstAvailableDeliveryDate2` + "`" + `\n * string \u003cdate\u003e\n * Second available date that Prime can deliver SIT service item.\n * firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 are required together\n * ` + "`" + `dateOfContact2` + "`" + `\n * string \u003cdate\u003e\n * Date of attempted contact delivery by the prime corresponding to ` + "`" + `timeMilitary2` + "`" + `\n * dateOfContact2, timeMilitary2, and firstAvailableDeliveryDate2 are required together\n * ` + "`" + `timeMilitary2` + "`" + `\n * string\\d{4}Z\n * Time of attempted contact corresponding to ` + "`" + `dateOfContact2` + "`" + `, in military format.\n * timeMilitary2, dateOfContact2, and firstAvailableDeliveryDate2 are required together\n\nWhen a DDFSIT is requested, the API will auto-create the following group of service items:\n * DDFSIT - Domestic destination 1st day SIT\n * DDASIT - Domestic destination Additional day SIT\n * DDDSIT - Domestic destination SIT delivery\n * DDSFSC - Domestic destination SIT fuel surcharge\n\n**NOTE** When providing the ` + "`" + `sitEntryDate` + "`" + ` value in the payload, please ensure that the date is not BEFORE\n` + "`" + `firstAvailableDeliveryDate1` + "`" + ` or ` + "`" + `firstAvailableDeliveryDate2` + "`" + `. If it is, you will receive an error response.\n\n**DDASIT**\n\n**Addt'l days destination SIT service item**. This represents an additional day of storage for the same item.\nAdditional DDASIT service items can be created and added to an existing shipment that **includes a DDFSIT service item**.\n\n---\n\n**` + "`" + `MTOServiceItemInternationalOriginSIT` + "`" + `**\n\nMTOServiceItemInternationalOriginSIT is a subtype of MTOServiceItem.\n\nThis model type describes a international origin SIT service item. Items can be created using this\nmodel type with the following codes:\n\n**IOFSIT**\n\n**1st day origin SIT service item**. When a IOFSIT is requested, the API will auto-create the following group of service items:\n * IOFSIT - International origin 1st day SIT\n * IOASIT - International origin Additional day SIT\n * IOPSIT - International origin SIT pickup\n * IOSFSC - International origin SIT fuel surcharge\n\n**IOASIT**\n\n**Addt'l days origin SIT service item**. This represents an additional day of storage for the same item.\nAdditional IOASIT service items can be created and added to an existing shipment that **includes a IOFSIT service item**.\n\n---\n\n**` + "`" + `MTOServiceItemInternationalDestSIT` + "`" + `**\n\nMTOServiceItemInternationalDestSIT is a subtype of MTOServiceItem.\n\nThis model type describes a international destination SIT service item. Items can be created using this\nmodel type with the following codes:\n\n**IDFSIT**\n\n**1st day destination SIT service item**.\n\nThese additional fields are optional for creating a IDFSIT:\n * ` + "`" + `firstAvailableDeliveryDate1` + "`" + `\n * string \u003cdate\u003e\n * First available date that Prime can deliver SIT service item.\n * firstAvailableDeliveryDate1, dateOfContact1, and timeMilitary1 are required together\n * ` + "`" + `dateOfContact1` + "`" + `\n * string \u003cdate\u003e\n * Date of attempted contact by the prime corresponding to ` + "`" + `timeMilitary1` + "`" + `\n * dateOfContact1, timeMilitary1, and firstAvailableDeliveryDate1 are required together\n * ` + "`" + `timeMilitary1` + "`" + `\n * string\\d{4}Z\n * Time of attempted contact corresponding to ` + "`" + `dateOfContact1` + "`" + `, in military format.\n * timeMilitary1, dateOfContact1, and firstAvailableDeliveryDate1 are required together\n * ` + "`" + `firstAvailableDeliveryDate2` + "`" + `\n * string \u003cdate\u003e\n * Second available date that Prime can deliver SIT service item.\n * firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 are required together\n * ` + "`" + `dateOfContact2` + "`" + `\n * string \u003cdate\u003e\n * Date of attempted contact delivery by the prime corresponding to ` + "`" + `timeMilitary2` + "`" + `\n * dateOfContact2, timeMilitary2, and firstAvailableDeliveryDate2 are required together\n * ` + "`" + `timeMilitary2` + "`" + `\n * string\\d{4}Z\n * Time of attempted contact corresponding to ` + "`" + `dateOfContact2` + "`" + `, in military format.\n * timeMilitary2, dateOfContact2, and firstAvailableDeliveryDate2 are required together\n\nWhen a IDFSIT is requested, the API will auto-create the following group of service items:\n * IDFSIT - International destination 1st day SIT\n * IDASIT - International destination Additional day SIT\n * IDDSIT - International destination SIT delivery\n * IDSFSC - International destination SIT fuel surcharge\n\n**NOTE** When providing the ` + "`" + `sitEntryDate` + "`" + ` value in the payload, please ensure that the date is not BEFORE\n` + "`" + `firstAvailableDeliveryDate1` + "`" + ` or ` + "`" + `firstAvailableDeliveryDate2` + "`" + `. If it is, you will receive an error response.\n\n**IDASIT**\n\n**Addt'l days destination SIT service item**. This represents an additional day of storage for the same item.\nAdditional IDASIT service items can be created and added to an existing shipment that **includes a IDFSIT service item**.\n", "consumes": [ "application/json" ], @@ -355,7 +355,7 @@ func init() { }, "/mto-service-items/{mtoServiceItemID}": { "patch": { - "description": "Updates MTOServiceItems after creation. Not all service items or fields may be updated, please see details below.\n\nThis endpoint supports different body definitions. In the modelType field below, select the modelType corresponding\n to the service item you wish to update and the documentation will update with the new definition.\n\n* Addresses: To update a destination service item's SIT destination final address, update the shipment delivery address.\nFor approved shipments, please use [updateShipmentDestinationAddress](#mtoShipment/updateShipmentDestinationAddress).\nFor shipments not yet approved, please use [updateMTOShipmentAddress](#mtoShipment/updateMTOShipmentAddress).\n\n* SIT Service Items: Take note that when updating ` + "`" + `sitCustomerContacted` + "`" + `, ` + "`" + `sitDepartureDate` + "`" + `, or ` + "`" + `sitRequestedDelivery` + "`" + `, we want\nthose to be updated on ` + "`" + `DOASIT` + "`" + ` (for origin SIT) and ` + "`" + `DDASIT` + "`" + ` (for destination SIT). If updating those values in other service\nitems, the office users will not have as much attention to those values.\n\nTo create a service item, please use [createMTOServiceItem](#mtoServiceItem/createMTOServiceItem)) endpoint.\n\n* Resubmitting rejected SIT/Accessorial service items: This endpoint will handle the logic of changing the status of rejected SIT/Accessorial service items from\nREJECTED to SUBMITTED. Please provide the ` + "`" + `requestedApprovalsRequestedStatus: true` + "`" + ` when resubmitting as this will give attention to the TOO to\nreview the resubmitted SIT/Accessorial service item. Another note, ` + "`" + `updateReason` + "`" + ` must have a different value than the current ` + "`" + `reason` + "`" + ` value on the service item.\nIf this value is not updated, then an error will be sent back.\n\nThe following SIT service items can be resubmitted following a rejection:\n- DDASIT\n- DDDSIT\n- DDFSIT\n- DOASIT\n- DOPSIT\n- DOFSIT\n- DDSFSC\n- DOSFSC\n\nThe following Accessorial service items can be resubmitted following a rejection:\n- IOSHUT\n- IDSHUT\n\nAt a MINIMUM, the payload for resubmitting a rejected SIT/Accessorial service item must look like this:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"reServiceCode\": \"DDFSIT\",\n \"updateReason\": \"A reason that differs from the previous reason\",\n \"modelType\": \"UpdateMTOServiceItemSIT\",\n \"requestApprovalsRequestedStatus\": true\n}\n` + "`" + `` + "`" + `` + "`" + `\n\nThe following service items allow you to update the Port that the shipment will use:\n- PODFSC (Port of Debarkation can be updated)\n- POEFSC (Port of Embarkation can be updated)\n\nAt a MINIMUM, the payload for updating the port should contain the reServiceCode (PODFSC or POEFSC), modelType (UpdateMTOServiceItemInternationalPortFSC), portCode, and id for the service item.\nPlease see the example payload below:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"id\": \"1ed224b6-c65e-4616-b88e-8304d26c9562\",\n \"modelType\": \"UpdateMTOServiceItemInternationalPortFSC\",\n \"portCode\": \"SEA\",\n \"reServiceCode\": \"POEFSC\"\n}\n` + "`" + `` + "`" + `` + "`" + `\n", + "description": "Updates MTOServiceItems after creation. Not all service items or fields may be updated, please see details below.\n\nThis endpoint supports different body definitions. In the modelType field below, select the modelType corresponding\n to the service item you wish to update and the documentation will update with the new definition.\n\n* Addresses: To update a destination service item's SIT destination final address, update the shipment delivery address.\nFor approved shipments, please use [updateShipmentDestinationAddress](#mtoShipment/updateShipmentDestinationAddress).\nFor shipments not yet approved, please use [updateMTOShipmentAddress](#mtoShipment/updateMTOShipmentAddress).\n\n* SIT Service Items: Take note that when updating ` + "`" + `sitCustomerContacted` + "`" + `, ` + "`" + `sitDepartureDate` + "`" + `, or ` + "`" + `sitRequestedDelivery` + "`" + `, we want\nthose to be updated on ` + "`" + `DOASIT` + "`" + ` (for origin SIT) and ` + "`" + `DDASIT` + "`" + ` (for destination SIT). If updating those values in other service\nitems, the office users will not have as much attention to those values.\n\nTo create a service item, please use [createMTOServiceItem](#mtoServiceItem/createMTOServiceItem)) endpoint.\n\n* Resubmitting rejected SIT/Accessorial service items: This endpoint will handle the logic of changing the status of rejected SIT/Accessorial service items from\nREJECTED to SUBMITTED. Please provide the ` + "`" + `requestedApprovalsRequestedStatus: true` + "`" + ` when resubmitting as this will give attention to the TOO to\nreview the resubmitted SIT/Accessorial service item. Another note, ` + "`" + `updateReason` + "`" + ` must have a different value than the current ` + "`" + `reason` + "`" + ` value on the service item.\nIf this value is not updated, then an error will be sent back.\n\nThe following SIT service items can be resubmitted following a rejection:\n- DDASIT\n- DDDSIT\n- DDFSIT\n- DOASIT\n- DOPSIT\n- DOFSIT\n- DDSFSC\n- DOSFSC\n- IDASIT\n- IDDSIT\n- IDFSIT\n- IOASIT\n- IOPSIT\n- IOFSIT\n- IDSFSC\n- IOSFSC\n\nThe following Accessorial service items can be resubmitted following a rejection:\n- IOSHUT\n- IDSHUT\n\nAt a MINIMUM, the payload for resubmitting a rejected SIT/Accessorial service item must look like this:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"reServiceCode\": \"DDFSIT\",\n \"updateReason\": \"A reason that differs from the previous reason\",\n \"modelType\": \"UpdateMTOServiceItemSIT\",\n \"requestApprovalsRequestedStatus\": true\n}\n` + "`" + `` + "`" + `` + "`" + `\n\nThe following service items allow you to update the Port that the shipment will use:\n- PODFSC (Port of Debarkation can be updated)\n- POEFSC (Port of Embarkation can be updated)\n\nAt a MINIMUM, the payload for updating the port should contain the reServiceCode (PODFSC or POEFSC), modelType (UpdateMTOServiceItemInternationalPortFSC), portCode, and id for the service item.\nPlease see the example payload below:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"id\": \"1ed224b6-c65e-4616-b88e-8304d26c9562\",\n \"modelType\": \"UpdateMTOServiceItemInternationalPortFSC\",\n \"portCode\": \"SEA\",\n \"reServiceCode\": \"POEFSC\"\n}\n` + "`" + `` + "`" + `` + "`" + `\n", "consumes": [ "application/json" ], @@ -3967,7 +3967,7 @@ func init() { "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" }, "params": { - "description": "This should be populated for the following service items:\n * DOASIT(Domestic origin Additional day SIT)\n * DDASIT(Domestic destination Additional day SIT)\n\nBoth take in the following param keys:\n * ` + "`" + `SITPaymentRequestStart` + "`" + `\n * ` + "`" + `SITPaymentRequestEnd` + "`" + `\n\nThe value of each is a date string in the format \"YYYY-MM-DD\" (e.g. \"2023-01-15\")\n", + "description": "This should be populated for the following service items:\n * DOASIT(Domestic origin Additional day SIT)\n * DDASIT(Domestic destination Additional day SIT)\n * IOASIT(International origin Additional day SIT)\n * IDASIT(International destination Additional day SIT)\n\nBoth take in the following param keys:\n * ` + "`" + `SITPaymentRequestStart` + "`" + `\n * ` + "`" + `SITPaymentRequestEnd` + "`" + `\n\nThe value of each is a date string in the format \"YYYY-MM-DD\" (e.g. \"2023-01-15\")\n", "type": "array", "items": { "type": "object", @@ -5249,7 +5249,7 @@ func init() { }, "/mto-service-items": { "post": { - "description": "Creates one or more MTOServiceItems. Not all service items may be created, please see details below.\n\nThis endpoint supports different body definitions. In the modelType field below, select the modelType corresponding\n to the service item you wish to create and the documentation will update with the new definition.\n\nUpon creation these items are associated with a Move Task Order and an MTO Shipment.\nThe request must include UUIDs for the MTO and MTO Shipment connected to this service item. Some service item types require\nadditional service items to be autogenerated when added - all created service items, autogenerated included,\nwill be returned in the response.\n\nTo update a service item, please use [updateMTOServiceItem](#operation/updateMTOServiceItem) endpoint.\n\n---\n\n**` + "`" + `MTOServiceItemOriginSIT` + "`" + `**\n\nMTOServiceItemOriginSIT is a subtype of MTOServiceItem.\n\nThis model type describes a domestic origin SIT service item. Items can be created using this\nmodel type with the following codes:\n\n**DOFSIT**\n\n**1st day origin SIT service item**. When a DOFSIT is requested, the API will auto-create the following group of service items:\n * DOFSIT - Domestic origin 1st day SIT\n * DOASIT - Domestic origin Additional day SIT\n * DOPSIT - Domestic origin SIT pickup\n * DOSFSC - Domestic origin SIT fuel surcharge\n\n**DOASIT**\n\n**Addt'l days origin SIT service item**. This represents an additional day of storage for the same item.\nAdditional DOASIT service items can be created and added to an existing shipment that **includes a DOFSIT service item**.\n\n---\n\n**` + "`" + `MTOServiceItemDestSIT` + "`" + `**\n\nMTOServiceItemDestSIT is a subtype of MTOServiceItem.\n\nThis model type describes a domestic destination SIT service item. Items can be created using this\nmodel type with the following codes:\n\n**DDFSIT**\n\n**1st day destination SIT service item**.\n\nThese additional fields are optional for creating a DDFSIT:\n * ` + "`" + `firstAvailableDeliveryDate1` + "`" + `\n * string \u003cdate\u003e\n * First available date that Prime can deliver SIT service item.\n * firstAvailableDeliveryDate1, dateOfContact1, and timeMilitary1 are required together\n * ` + "`" + `dateOfContact1` + "`" + `\n * string \u003cdate\u003e\n * Date of attempted contact by the prime corresponding to ` + "`" + `timeMilitary1` + "`" + `\n * dateOfContact1, timeMilitary1, and firstAvailableDeliveryDate1 are required together\n * ` + "`" + `timeMilitary1` + "`" + `\n * string\\d{4}Z\n * Time of attempted contact corresponding to ` + "`" + `dateOfContact1` + "`" + `, in military format.\n * timeMilitary1, dateOfContact1, and firstAvailableDeliveryDate1 are required together\n * ` + "`" + `firstAvailableDeliveryDate2` + "`" + `\n * string \u003cdate\u003e\n * Second available date that Prime can deliver SIT service item.\n * firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 are required together\n * ` + "`" + `dateOfContact2` + "`" + `\n * string \u003cdate\u003e\n * Date of attempted contact delivery by the prime corresponding to ` + "`" + `timeMilitary2` + "`" + `\n * dateOfContact2, timeMilitary2, and firstAvailableDeliveryDate2 are required together\n * ` + "`" + `timeMilitary2` + "`" + `\n * string\\d{4}Z\n * Time of attempted contact corresponding to ` + "`" + `dateOfContact2` + "`" + `, in military format.\n * timeMilitary2, dateOfContact2, and firstAvailableDeliveryDate2 are required together\n\nWhen a DDFSIT is requested, the API will auto-create the following group of service items:\n * DDFSIT - Domestic destination 1st day SIT\n * DDASIT - Domestic destination Additional day SIT\n * DDDSIT - Domestic destination SIT delivery\n * DDSFSC - Domestic destination SIT fuel surcharge\n\n**NOTE** When providing the ` + "`" + `sitEntryDate` + "`" + ` value in the payload, please ensure that the date is not BEFORE\n` + "`" + `firstAvailableDeliveryDate1` + "`" + ` or ` + "`" + `firstAvailableDeliveryDate2` + "`" + `. If it is, you will receive an error response.\n\n**DDASIT**\n\n**Addt'l days destination SIT service item**. This represents an additional day of storage for the same item.\nAdditional DDASIT service items can be created and added to an existing shipment that **includes a DDFSIT service item**.\n", + "description": "Creates one or more MTOServiceItems. Not all service items may be created, please see details below.\n\nThis endpoint supports different body definitions. In the modelType field below, select the modelType corresponding\n to the service item you wish to create and the documentation will update with the new definition.\n\nUpon creation these items are associated with a Move Task Order and an MTO Shipment.\nThe request must include UUIDs for the MTO and MTO Shipment connected to this service item. Some service item types require\nadditional service items to be autogenerated when added - all created service items, autogenerated included,\nwill be returned in the response.\n\nTo update a service item, please use [updateMTOServiceItem](#operation/updateMTOServiceItem) endpoint.\n\n---\n\n**` + "`" + `MTOServiceItemOriginSIT` + "`" + `**\n\nMTOServiceItemOriginSIT is a subtype of MTOServiceItem.\n\nThis model type describes a domestic origin SIT service item. Items can be created using this\nmodel type with the following codes:\n\n**DOFSIT**\n\n**1st day origin SIT service item**. When a DOFSIT is requested, the API will auto-create the following group of service items:\n * DOFSIT - Domestic origin 1st day SIT\n * DOASIT - Domestic origin Additional day SIT\n * DOPSIT - Domestic origin SIT pickup\n * DOSFSC - Domestic origin SIT fuel surcharge\n\n**DOASIT**\n\n**Addt'l days origin SIT service item**. This represents an additional day of storage for the same item.\nAdditional DOASIT service items can be created and added to an existing shipment that **includes a DOFSIT service item**.\n\n---\n\n**` + "`" + `MTOServiceItemDestSIT` + "`" + `**\n\nMTOServiceItemDestSIT is a subtype of MTOServiceItem.\n\nThis model type describes a domestic destination SIT service item. Items can be created using this\nmodel type with the following codes:\n\n**DDFSIT**\n\n**1st day destination SIT service item**.\n\nThese additional fields are optional for creating a DDFSIT:\n * ` + "`" + `firstAvailableDeliveryDate1` + "`" + `\n * string \u003cdate\u003e\n * First available date that Prime can deliver SIT service item.\n * firstAvailableDeliveryDate1, dateOfContact1, and timeMilitary1 are required together\n * ` + "`" + `dateOfContact1` + "`" + `\n * string \u003cdate\u003e\n * Date of attempted contact by the prime corresponding to ` + "`" + `timeMilitary1` + "`" + `\n * dateOfContact1, timeMilitary1, and firstAvailableDeliveryDate1 are required together\n * ` + "`" + `timeMilitary1` + "`" + `\n * string\\d{4}Z\n * Time of attempted contact corresponding to ` + "`" + `dateOfContact1` + "`" + `, in military format.\n * timeMilitary1, dateOfContact1, and firstAvailableDeliveryDate1 are required together\n * ` + "`" + `firstAvailableDeliveryDate2` + "`" + `\n * string \u003cdate\u003e\n * Second available date that Prime can deliver SIT service item.\n * firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 are required together\n * ` + "`" + `dateOfContact2` + "`" + `\n * string \u003cdate\u003e\n * Date of attempted contact delivery by the prime corresponding to ` + "`" + `timeMilitary2` + "`" + `\n * dateOfContact2, timeMilitary2, and firstAvailableDeliveryDate2 are required together\n * ` + "`" + `timeMilitary2` + "`" + `\n * string\\d{4}Z\n * Time of attempted contact corresponding to ` + "`" + `dateOfContact2` + "`" + `, in military format.\n * timeMilitary2, dateOfContact2, and firstAvailableDeliveryDate2 are required together\n\nWhen a DDFSIT is requested, the API will auto-create the following group of service items:\n * DDFSIT - Domestic destination 1st day SIT\n * DDASIT - Domestic destination Additional day SIT\n * DDDSIT - Domestic destination SIT delivery\n * DDSFSC - Domestic destination SIT fuel surcharge\n\n**NOTE** When providing the ` + "`" + `sitEntryDate` + "`" + ` value in the payload, please ensure that the date is not BEFORE\n` + "`" + `firstAvailableDeliveryDate1` + "`" + ` or ` + "`" + `firstAvailableDeliveryDate2` + "`" + `. If it is, you will receive an error response.\n\n**DDASIT**\n\n**Addt'l days destination SIT service item**. This represents an additional day of storage for the same item.\nAdditional DDASIT service items can be created and added to an existing shipment that **includes a DDFSIT service item**.\n\n---\n\n**` + "`" + `MTOServiceItemInternationalOriginSIT` + "`" + `**\n\nMTOServiceItemInternationalOriginSIT is a subtype of MTOServiceItem.\n\nThis model type describes a international origin SIT service item. Items can be created using this\nmodel type with the following codes:\n\n**IOFSIT**\n\n**1st day origin SIT service item**. When a IOFSIT is requested, the API will auto-create the following group of service items:\n * IOFSIT - International origin 1st day SIT\n * IOASIT - International origin Additional day SIT\n * IOPSIT - International origin SIT pickup\n * IOSFSC - International origin SIT fuel surcharge\n\n**IOASIT**\n\n**Addt'l days origin SIT service item**. This represents an additional day of storage for the same item.\nAdditional IOASIT service items can be created and added to an existing shipment that **includes a IOFSIT service item**.\n\n---\n\n**` + "`" + `MTOServiceItemInternationalDestSIT` + "`" + `**\n\nMTOServiceItemInternationalDestSIT is a subtype of MTOServiceItem.\n\nThis model type describes a international destination SIT service item. Items can be created using this\nmodel type with the following codes:\n\n**IDFSIT**\n\n**1st day destination SIT service item**.\n\nThese additional fields are optional for creating a IDFSIT:\n * ` + "`" + `firstAvailableDeliveryDate1` + "`" + `\n * string \u003cdate\u003e\n * First available date that Prime can deliver SIT service item.\n * firstAvailableDeliveryDate1, dateOfContact1, and timeMilitary1 are required together\n * ` + "`" + `dateOfContact1` + "`" + `\n * string \u003cdate\u003e\n * Date of attempted contact by the prime corresponding to ` + "`" + `timeMilitary1` + "`" + `\n * dateOfContact1, timeMilitary1, and firstAvailableDeliveryDate1 are required together\n * ` + "`" + `timeMilitary1` + "`" + `\n * string\\d{4}Z\n * Time of attempted contact corresponding to ` + "`" + `dateOfContact1` + "`" + `, in military format.\n * timeMilitary1, dateOfContact1, and firstAvailableDeliveryDate1 are required together\n * ` + "`" + `firstAvailableDeliveryDate2` + "`" + `\n * string \u003cdate\u003e\n * Second available date that Prime can deliver SIT service item.\n * firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 are required together\n * ` + "`" + `dateOfContact2` + "`" + `\n * string \u003cdate\u003e\n * Date of attempted contact delivery by the prime corresponding to ` + "`" + `timeMilitary2` + "`" + `\n * dateOfContact2, timeMilitary2, and firstAvailableDeliveryDate2 are required together\n * ` + "`" + `timeMilitary2` + "`" + `\n * string\\d{4}Z\n * Time of attempted contact corresponding to ` + "`" + `dateOfContact2` + "`" + `, in military format.\n * timeMilitary2, dateOfContact2, and firstAvailableDeliveryDate2 are required together\n\nWhen a IDFSIT is requested, the API will auto-create the following group of service items:\n * IDFSIT - International destination 1st day SIT\n * IDASIT - International destination Additional day SIT\n * IDDSIT - International destination SIT delivery\n * IDSFSC - International destination SIT fuel surcharge\n\n**NOTE** When providing the ` + "`" + `sitEntryDate` + "`" + ` value in the payload, please ensure that the date is not BEFORE\n` + "`" + `firstAvailableDeliveryDate1` + "`" + ` or ` + "`" + `firstAvailableDeliveryDate2` + "`" + `. If it is, you will receive an error response.\n\n**IDASIT**\n\n**Addt'l days destination SIT service item**. This represents an additional day of storage for the same item.\nAdditional IDASIT service items can be created and added to an existing shipment that **includes a IDFSIT service item**.\n", "consumes": [ "application/json" ], @@ -5327,7 +5327,7 @@ func init() { }, "/mto-service-items/{mtoServiceItemID}": { "patch": { - "description": "Updates MTOServiceItems after creation. Not all service items or fields may be updated, please see details below.\n\nThis endpoint supports different body definitions. In the modelType field below, select the modelType corresponding\n to the service item you wish to update and the documentation will update with the new definition.\n\n* Addresses: To update a destination service item's SIT destination final address, update the shipment delivery address.\nFor approved shipments, please use [updateShipmentDestinationAddress](#mtoShipment/updateShipmentDestinationAddress).\nFor shipments not yet approved, please use [updateMTOShipmentAddress](#mtoShipment/updateMTOShipmentAddress).\n\n* SIT Service Items: Take note that when updating ` + "`" + `sitCustomerContacted` + "`" + `, ` + "`" + `sitDepartureDate` + "`" + `, or ` + "`" + `sitRequestedDelivery` + "`" + `, we want\nthose to be updated on ` + "`" + `DOASIT` + "`" + ` (for origin SIT) and ` + "`" + `DDASIT` + "`" + ` (for destination SIT). If updating those values in other service\nitems, the office users will not have as much attention to those values.\n\nTo create a service item, please use [createMTOServiceItem](#mtoServiceItem/createMTOServiceItem)) endpoint.\n\n* Resubmitting rejected SIT/Accessorial service items: This endpoint will handle the logic of changing the status of rejected SIT/Accessorial service items from\nREJECTED to SUBMITTED. Please provide the ` + "`" + `requestedApprovalsRequestedStatus: true` + "`" + ` when resubmitting as this will give attention to the TOO to\nreview the resubmitted SIT/Accessorial service item. Another note, ` + "`" + `updateReason` + "`" + ` must have a different value than the current ` + "`" + `reason` + "`" + ` value on the service item.\nIf this value is not updated, then an error will be sent back.\n\nThe following SIT service items can be resubmitted following a rejection:\n- DDASIT\n- DDDSIT\n- DDFSIT\n- DOASIT\n- DOPSIT\n- DOFSIT\n- DDSFSC\n- DOSFSC\n\nThe following Accessorial service items can be resubmitted following a rejection:\n- IOSHUT\n- IDSHUT\n\nAt a MINIMUM, the payload for resubmitting a rejected SIT/Accessorial service item must look like this:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"reServiceCode\": \"DDFSIT\",\n \"updateReason\": \"A reason that differs from the previous reason\",\n \"modelType\": \"UpdateMTOServiceItemSIT\",\n \"requestApprovalsRequestedStatus\": true\n}\n` + "`" + `` + "`" + `` + "`" + `\n\nThe following service items allow you to update the Port that the shipment will use:\n- PODFSC (Port of Debarkation can be updated)\n- POEFSC (Port of Embarkation can be updated)\n\nAt a MINIMUM, the payload for updating the port should contain the reServiceCode (PODFSC or POEFSC), modelType (UpdateMTOServiceItemInternationalPortFSC), portCode, and id for the service item.\nPlease see the example payload below:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"id\": \"1ed224b6-c65e-4616-b88e-8304d26c9562\",\n \"modelType\": \"UpdateMTOServiceItemInternationalPortFSC\",\n \"portCode\": \"SEA\",\n \"reServiceCode\": \"POEFSC\"\n}\n` + "`" + `` + "`" + `` + "`" + `\n", + "description": "Updates MTOServiceItems after creation. Not all service items or fields may be updated, please see details below.\n\nThis endpoint supports different body definitions. In the modelType field below, select the modelType corresponding\n to the service item you wish to update and the documentation will update with the new definition.\n\n* Addresses: To update a destination service item's SIT destination final address, update the shipment delivery address.\nFor approved shipments, please use [updateShipmentDestinationAddress](#mtoShipment/updateShipmentDestinationAddress).\nFor shipments not yet approved, please use [updateMTOShipmentAddress](#mtoShipment/updateMTOShipmentAddress).\n\n* SIT Service Items: Take note that when updating ` + "`" + `sitCustomerContacted` + "`" + `, ` + "`" + `sitDepartureDate` + "`" + `, or ` + "`" + `sitRequestedDelivery` + "`" + `, we want\nthose to be updated on ` + "`" + `DOASIT` + "`" + ` (for origin SIT) and ` + "`" + `DDASIT` + "`" + ` (for destination SIT). If updating those values in other service\nitems, the office users will not have as much attention to those values.\n\nTo create a service item, please use [createMTOServiceItem](#mtoServiceItem/createMTOServiceItem)) endpoint.\n\n* Resubmitting rejected SIT/Accessorial service items: This endpoint will handle the logic of changing the status of rejected SIT/Accessorial service items from\nREJECTED to SUBMITTED. Please provide the ` + "`" + `requestedApprovalsRequestedStatus: true` + "`" + ` when resubmitting as this will give attention to the TOO to\nreview the resubmitted SIT/Accessorial service item. Another note, ` + "`" + `updateReason` + "`" + ` must have a different value than the current ` + "`" + `reason` + "`" + ` value on the service item.\nIf this value is not updated, then an error will be sent back.\n\nThe following SIT service items can be resubmitted following a rejection:\n- DDASIT\n- DDDSIT\n- DDFSIT\n- DOASIT\n- DOPSIT\n- DOFSIT\n- DDSFSC\n- DOSFSC\n- IDASIT\n- IDDSIT\n- IDFSIT\n- IOASIT\n- IOPSIT\n- IOFSIT\n- IDSFSC\n- IOSFSC\n\nThe following Accessorial service items can be resubmitted following a rejection:\n- IOSHUT\n- IDSHUT\n\nAt a MINIMUM, the payload for resubmitting a rejected SIT/Accessorial service item must look like this:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"reServiceCode\": \"DDFSIT\",\n \"updateReason\": \"A reason that differs from the previous reason\",\n \"modelType\": \"UpdateMTOServiceItemSIT\",\n \"requestApprovalsRequestedStatus\": true\n}\n` + "`" + `` + "`" + `` + "`" + `\n\nThe following service items allow you to update the Port that the shipment will use:\n- PODFSC (Port of Debarkation can be updated)\n- POEFSC (Port of Embarkation can be updated)\n\nAt a MINIMUM, the payload for updating the port should contain the reServiceCode (PODFSC or POEFSC), modelType (UpdateMTOServiceItemInternationalPortFSC), portCode, and id for the service item.\nPlease see the example payload below:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"id\": \"1ed224b6-c65e-4616-b88e-8304d26c9562\",\n \"modelType\": \"UpdateMTOServiceItemInternationalPortFSC\",\n \"portCode\": \"SEA\",\n \"reServiceCode\": \"POEFSC\"\n}\n` + "`" + `` + "`" + `` + "`" + `\n", "consumes": [ "application/json" ], @@ -9214,7 +9214,7 @@ func init() { "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" }, "params": { - "description": "This should be populated for the following service items:\n * DOASIT(Domestic origin Additional day SIT)\n * DDASIT(Domestic destination Additional day SIT)\n\nBoth take in the following param keys:\n * ` + "`" + `SITPaymentRequestStart` + "`" + `\n * ` + "`" + `SITPaymentRequestEnd` + "`" + `\n\nThe value of each is a date string in the format \"YYYY-MM-DD\" (e.g. \"2023-01-15\")\n", + "description": "This should be populated for the following service items:\n * DOASIT(Domestic origin Additional day SIT)\n * DDASIT(Domestic destination Additional day SIT)\n * IOASIT(International origin Additional day SIT)\n * IDASIT(International destination Additional day SIT)\n\nBoth take in the following param keys:\n * ` + "`" + `SITPaymentRequestStart` + "`" + `\n * ` + "`" + `SITPaymentRequestEnd` + "`" + `\n\nThe value of each is a date string in the format \"YYYY-MM-DD\" (e.g. \"2023-01-15\")\n", "type": "array", "items": { "$ref": "#/definitions/ServiceItemParamsItems0" diff --git a/pkg/gen/primeapi/primeoperations/mto_service_item/create_m_t_o_service_item.go b/pkg/gen/primeapi/primeoperations/mto_service_item/create_m_t_o_service_item.go index 85456161f0c..88adde61f94 100644 --- a/pkg/gen/primeapi/primeoperations/mto_service_item/create_m_t_o_service_item.go +++ b/pkg/gen/primeapi/primeoperations/mto_service_item/create_m_t_o_service_item.go @@ -121,6 +121,81 @@ When a DDFSIT is requested, the API will auto-create the following group of serv **Addt'l days destination SIT service item**. This represents an additional day of storage for the same item. Additional DDASIT service items can be created and added to an existing shipment that **includes a DDFSIT service item**. + +--- + +**`MTOServiceItemInternationalOriginSIT`** + +MTOServiceItemInternationalOriginSIT is a subtype of MTOServiceItem. + +This model type describes a international origin SIT service item. Items can be created using this +model type with the following codes: + +**IOFSIT** + +**1st day origin SIT service item**. When a IOFSIT is requested, the API will auto-create the following group of service items: + - IOFSIT - International origin 1st day SIT + - IOASIT - International origin Additional day SIT + - IOPSIT - International origin SIT pickup + - IOSFSC - International origin SIT fuel surcharge + +**IOASIT** + +**Addt'l days origin SIT service item**. This represents an additional day of storage for the same item. +Additional IOASIT service items can be created and added to an existing shipment that **includes a IOFSIT service item**. + +--- + +**`MTOServiceItemInternationalDestSIT`** + +MTOServiceItemInternationalDestSIT is a subtype of MTOServiceItem. + +This model type describes a international destination SIT service item. Items can be created using this +model type with the following codes: + +**IDFSIT** + +**1st day destination SIT service item**. + +These additional fields are optional for creating a IDFSIT: + - `firstAvailableDeliveryDate1` + - string + - First available date that Prime can deliver SIT service item. + - firstAvailableDeliveryDate1, dateOfContact1, and timeMilitary1 are required together + - `dateOfContact1` + - string + - Date of attempted contact by the prime corresponding to `timeMilitary1` + - dateOfContact1, timeMilitary1, and firstAvailableDeliveryDate1 are required together + - `timeMilitary1` + - string\d{4}Z + - Time of attempted contact corresponding to `dateOfContact1`, in military format. + - timeMilitary1, dateOfContact1, and firstAvailableDeliveryDate1 are required together + - `firstAvailableDeliveryDate2` + - string + - Second available date that Prime can deliver SIT service item. + - firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 are required together + - `dateOfContact2` + - string + - Date of attempted contact delivery by the prime corresponding to `timeMilitary2` + - dateOfContact2, timeMilitary2, and firstAvailableDeliveryDate2 are required together + - `timeMilitary2` + - string\d{4}Z + - Time of attempted contact corresponding to `dateOfContact2`, in military format. + - timeMilitary2, dateOfContact2, and firstAvailableDeliveryDate2 are required together + +When a IDFSIT is requested, the API will auto-create the following group of service items: + - IDFSIT - International destination 1st day SIT + - IDASIT - International destination Additional day SIT + - IDDSIT - International destination SIT delivery + - IDSFSC - International destination SIT fuel surcharge + +**NOTE** When providing the `sitEntryDate` value in the payload, please ensure that the date is not BEFORE +`firstAvailableDeliveryDate1` or `firstAvailableDeliveryDate2`. If it is, you will receive an error response. + +**IDASIT** + +**Addt'l days destination SIT service item**. This represents an additional day of storage for the same item. +Additional IDASIT service items can be created and added to an existing shipment that **includes a IDFSIT service item**. */ type CreateMTOServiceItem struct { Context *middleware.Context diff --git a/pkg/gen/primeapi/primeoperations/mto_service_item/update_m_t_o_service_item.go b/pkg/gen/primeapi/primeoperations/mto_service_item/update_m_t_o_service_item.go index 8bfdb75c0f7..c124cef0d61 100644 --- a/pkg/gen/primeapi/primeoperations/mto_service_item/update_m_t_o_service_item.go +++ b/pkg/gen/primeapi/primeoperations/mto_service_item/update_m_t_o_service_item.go @@ -64,6 +64,14 @@ The following SIT service items can be resubmitted following a rejection: - DOFSIT - DDSFSC - DOSFSC +- IDASIT +- IDDSIT +- IDFSIT +- IOASIT +- IOPSIT +- IOFSIT +- IDSFSC +- IOSFSC The following Accessorial service items can be resubmitted following a rejection: - IOSHUT diff --git a/pkg/gen/primeclient/mto_service_item/mto_service_item_client.go b/pkg/gen/primeclient/mto_service_item/mto_service_item_client.go index 9fe2fa1212d..f0ca03af5b5 100644 --- a/pkg/gen/primeclient/mto_service_item/mto_service_item_client.go +++ b/pkg/gen/primeclient/mto_service_item/mto_service_item_client.go @@ -129,6 +129,81 @@ When a DDFSIT is requested, the API will auto-create the following group of serv **Addt'l days destination SIT service item**. This represents an additional day of storage for the same item. Additional DDASIT service items can be created and added to an existing shipment that **includes a DDFSIT service item**. + +--- + +**`MTOServiceItemInternationalOriginSIT`** + +MTOServiceItemInternationalOriginSIT is a subtype of MTOServiceItem. + +This model type describes a international origin SIT service item. Items can be created using this +model type with the following codes: + +**IOFSIT** + +**1st day origin SIT service item**. When a IOFSIT is requested, the API will auto-create the following group of service items: + - IOFSIT - International origin 1st day SIT + - IOASIT - International origin Additional day SIT + - IOPSIT - International origin SIT pickup + - IOSFSC - International origin SIT fuel surcharge + +**IOASIT** + +**Addt'l days origin SIT service item**. This represents an additional day of storage for the same item. +Additional IOASIT service items can be created and added to an existing shipment that **includes a IOFSIT service item**. + +--- + +**`MTOServiceItemInternationalDestSIT`** + +MTOServiceItemInternationalDestSIT is a subtype of MTOServiceItem. + +This model type describes a international destination SIT service item. Items can be created using this +model type with the following codes: + +**IDFSIT** + +**1st day destination SIT service item**. + +These additional fields are optional for creating a IDFSIT: + - `firstAvailableDeliveryDate1` + - string + - First available date that Prime can deliver SIT service item. + - firstAvailableDeliveryDate1, dateOfContact1, and timeMilitary1 are required together + - `dateOfContact1` + - string + - Date of attempted contact by the prime corresponding to `timeMilitary1` + - dateOfContact1, timeMilitary1, and firstAvailableDeliveryDate1 are required together + - `timeMilitary1` + - string\d{4}Z + - Time of attempted contact corresponding to `dateOfContact1`, in military format. + - timeMilitary1, dateOfContact1, and firstAvailableDeliveryDate1 are required together + - `firstAvailableDeliveryDate2` + - string + - Second available date that Prime can deliver SIT service item. + - firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 are required together + - `dateOfContact2` + - string + - Date of attempted contact delivery by the prime corresponding to `timeMilitary2` + - dateOfContact2, timeMilitary2, and firstAvailableDeliveryDate2 are required together + - `timeMilitary2` + - string\d{4}Z + - Time of attempted contact corresponding to `dateOfContact2`, in military format. + - timeMilitary2, dateOfContact2, and firstAvailableDeliveryDate2 are required together + +When a IDFSIT is requested, the API will auto-create the following group of service items: + - IDFSIT - International destination 1st day SIT + - IDASIT - International destination Additional day SIT + - IDDSIT - International destination SIT delivery + - IDSFSC - International destination SIT fuel surcharge + +**NOTE** When providing the `sitEntryDate` value in the payload, please ensure that the date is not BEFORE +`firstAvailableDeliveryDate1` or `firstAvailableDeliveryDate2`. If it is, you will receive an error response. + +**IDASIT** + +**Addt'l days destination SIT service item**. This represents an additional day of storage for the same item. +Additional IDASIT service items can be created and added to an existing shipment that **includes a IDFSIT service item**. */ func (a *Client) CreateMTOServiceItem(params *CreateMTOServiceItemParams, opts ...ClientOption) (*CreateMTOServiceItemOK, error) { // TODO: Validate the params before sending @@ -247,6 +322,14 @@ The following SIT service items can be resubmitted following a rejection: - DOFSIT - DDSFSC - DOSFSC +- IDASIT +- IDDSIT +- IDFSIT +- IOASIT +- IOPSIT +- IOFSIT +- IDSFSC +- IOSFSC The following Accessorial service items can be resubmitted following a rejection: - IOSHUT diff --git a/pkg/gen/primemessages/service_item.go b/pkg/gen/primemessages/service_item.go index 9f41d0b13e2..6c29dbc0d40 100644 --- a/pkg/gen/primemessages/service_item.go +++ b/pkg/gen/primemessages/service_item.go @@ -32,6 +32,8 @@ type ServiceItem struct { // This should be populated for the following service items: // * DOASIT(Domestic origin Additional day SIT) // * DDASIT(Domestic destination Additional day SIT) + // * IOASIT(International origin Additional day SIT) + // * IDASIT(International destination Additional day SIT) // // Both take in the following param keys: // * `SITPaymentRequestStart` diff --git a/pkg/gen/primev2api/embedded_spec.go b/pkg/gen/primev2api/embedded_spec.go index 373d03b9d77..76af942d426 100644 --- a/pkg/gen/primev2api/embedded_spec.go +++ b/pkg/gen/primev2api/embedded_spec.go @@ -3341,7 +3341,7 @@ func init() { ] }, "UpdateMTOServiceItemModelType": { - "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * IDSHUT - UpdateMTOServiceItemInternationalShuttle\n * IOSHUT - UpdateMTOServiceItemInternationalShuttle\n\nThe documentation will then update with the supported fields.\n", + "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * IDDSIT - UpdateMTOServiceItemSIT\n * IOPSIT - UpdateMTOServiceItemSIT\n * IOASIT - UpdateMTOServiceItemSIT\n * IOFSIT - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * IDSHUT - UpdateMTOServiceItemInternationalShuttle\n * IOSHUT - UpdateMTOServiceItemInternationalShuttle\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "UpdateMTOServiceItemSIT", @@ -3389,7 +3389,11 @@ func init() { "DDDSIT", "DOPSIT", "DOASIT", - "DOFSIT" + "DOFSIT", + "IDDSIT", + "IOPSIT", + "IOASIT", + "IOFSIT" ] }, "requestApprovalsRequestedStatus": { @@ -7269,7 +7273,7 @@ func init() { ] }, "UpdateMTOServiceItemModelType": { - "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * IDSHUT - UpdateMTOServiceItemInternationalShuttle\n * IOSHUT - UpdateMTOServiceItemInternationalShuttle\n\nThe documentation will then update with the supported fields.\n", + "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * IDDSIT - UpdateMTOServiceItemSIT\n * IOPSIT - UpdateMTOServiceItemSIT\n * IOASIT - UpdateMTOServiceItemSIT\n * IOFSIT - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * IDSHUT - UpdateMTOServiceItemInternationalShuttle\n * IOSHUT - UpdateMTOServiceItemInternationalShuttle\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "UpdateMTOServiceItemSIT", @@ -7317,7 +7321,11 @@ func init() { "DDDSIT", "DOPSIT", "DOASIT", - "DOFSIT" + "DOFSIT", + "IDDSIT", + "IOPSIT", + "IOASIT", + "IOFSIT" ] }, "requestApprovalsRequestedStatus": { diff --git a/pkg/gen/primev2messages/update_m_t_o_service_item_model_type.go b/pkg/gen/primev2messages/update_m_t_o_service_item_model_type.go index 8b865cfec1f..ce2195310c9 100644 --- a/pkg/gen/primev2messages/update_m_t_o_service_item_model_type.go +++ b/pkg/gen/primev2messages/update_m_t_o_service_item_model_type.go @@ -19,6 +19,10 @@ import ( // - DOPSIT - UpdateMTOServiceItemSIT // - DOASIT - UpdateMTOServiceItemSIT // - DOFSIT - UpdateMTOServiceItemSIT +// - IDDSIT - UpdateMTOServiceItemSIT +// - IOPSIT - UpdateMTOServiceItemSIT +// - IOASIT - UpdateMTOServiceItemSIT +// - IOFSIT - UpdateMTOServiceItemSIT // - DDSHUT - UpdateMTOServiceItemShuttle // - DOSHUT - UpdateMTOServiceItemShuttle // - IDSHUT - UpdateMTOServiceItemInternationalShuttle diff --git a/pkg/gen/primev2messages/update_m_t_o_service_item_s_i_t.go b/pkg/gen/primev2messages/update_m_t_o_service_item_s_i_t.go index 558e84ba19c..9accd54333f 100644 --- a/pkg/gen/primev2messages/update_m_t_o_service_item_s_i_t.go +++ b/pkg/gen/primev2messages/update_m_t_o_service_item_s_i_t.go @@ -39,7 +39,7 @@ type UpdateMTOServiceItemSIT struct { FirstAvailableDeliveryDate2 *strfmt.Date `json:"firstAvailableDeliveryDate2,omitempty"` // Service code allowed for this model type. - // Enum: [DDDSIT DOPSIT DOASIT DOFSIT] + // Enum: [DDDSIT DOPSIT DOASIT DOFSIT IDDSIT IOPSIT IOASIT IOFSIT] ReServiceCode string `json:"reServiceCode,omitempty"` // Indicates if "Approvals Requested" status is being requested. @@ -123,7 +123,7 @@ func (m *UpdateMTOServiceItemSIT) UnmarshalJSON(raw []byte) error { FirstAvailableDeliveryDate2 *strfmt.Date `json:"firstAvailableDeliveryDate2,omitempty"` // Service code allowed for this model type. - // Enum: [DDDSIT DOPSIT DOASIT DOFSIT] + // Enum: [DDDSIT DOPSIT DOASIT DOFSIT IDDSIT IOPSIT IOASIT IOFSIT] ReServiceCode string `json:"reServiceCode,omitempty"` // Indicates if "Approvals Requested" status is being requested. @@ -242,7 +242,7 @@ func (m UpdateMTOServiceItemSIT) MarshalJSON() ([]byte, error) { FirstAvailableDeliveryDate2 *strfmt.Date `json:"firstAvailableDeliveryDate2,omitempty"` // Service code allowed for this model type. - // Enum: [DDDSIT DOPSIT DOASIT DOFSIT] + // Enum: [DDDSIT DOPSIT DOASIT DOFSIT IDDSIT IOPSIT IOASIT IOFSIT] ReServiceCode string `json:"reServiceCode,omitempty"` // Indicates if "Approvals Requested" status is being requested. @@ -471,7 +471,7 @@ var updateMTOServiceItemSITTypeReServiceCodePropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["DDDSIT","DOPSIT","DOASIT","DOFSIT"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["DDDSIT","DOPSIT","DOASIT","DOFSIT","IDDSIT","IOPSIT","IOASIT","IOFSIT"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/primev3api/embedded_spec.go b/pkg/gen/primev3api/embedded_spec.go index ee50749ebd6..de8a61df0e4 100644 --- a/pkg/gen/primev3api/embedded_spec.go +++ b/pkg/gen/primev3api/embedded_spec.go @@ -3959,7 +3959,7 @@ func init() { ] }, "UpdateMTOServiceItemModelType": { - "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * IDSHUT - UpdateMTOServiceItemInternationalShuttle\n * IOSHUT - UpdateMTOServiceItemInternationalShuttle\n\nThe documentation will then update with the supported fields.\n", + "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * IDDSIT - UpdateMTOServiceItemSIT\n * IOPSIT - UpdateMTOServiceItemSIT\n * IOASIT - UpdateMTOServiceItemSIT\n * IOFSIT - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * IDSHUT - UpdateMTOServiceItemInternationalShuttle\n * IOSHUT - UpdateMTOServiceItemInternationalShuttle\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "UpdateMTOServiceItemSIT", @@ -4007,7 +4007,11 @@ func init() { "DDDSIT", "DOPSIT", "DOASIT", - "DOFSIT" + "DOFSIT", + "IDDSIT", + "IOPSIT", + "IOASIT", + "IOFSIT" ] }, "requestApprovalsRequestedStatus": { @@ -8597,7 +8601,7 @@ func init() { ] }, "UpdateMTOServiceItemModelType": { - "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * IDSHUT - UpdateMTOServiceItemInternationalShuttle\n * IOSHUT - UpdateMTOServiceItemInternationalShuttle\n\nThe documentation will then update with the supported fields.\n", + "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * IDDSIT - UpdateMTOServiceItemSIT\n * IOPSIT - UpdateMTOServiceItemSIT\n * IOASIT - UpdateMTOServiceItemSIT\n * IOFSIT - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * IDSHUT - UpdateMTOServiceItemInternationalShuttle\n * IOSHUT - UpdateMTOServiceItemInternationalShuttle\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "UpdateMTOServiceItemSIT", @@ -8645,7 +8649,11 @@ func init() { "DDDSIT", "DOPSIT", "DOASIT", - "DOFSIT" + "DOFSIT", + "IDDSIT", + "IOPSIT", + "IOASIT", + "IOFSIT" ] }, "requestApprovalsRequestedStatus": { diff --git a/pkg/gen/primev3messages/update_m_t_o_service_item_model_type.go b/pkg/gen/primev3messages/update_m_t_o_service_item_model_type.go index a1bc8152ec6..e1ad72b1e4e 100644 --- a/pkg/gen/primev3messages/update_m_t_o_service_item_model_type.go +++ b/pkg/gen/primev3messages/update_m_t_o_service_item_model_type.go @@ -19,6 +19,10 @@ import ( // - DOPSIT - UpdateMTOServiceItemSIT // - DOASIT - UpdateMTOServiceItemSIT // - DOFSIT - UpdateMTOServiceItemSIT +// - IDDSIT - UpdateMTOServiceItemSIT +// - IOPSIT - UpdateMTOServiceItemSIT +// - IOASIT - UpdateMTOServiceItemSIT +// - IOFSIT - UpdateMTOServiceItemSIT // - DDSHUT - UpdateMTOServiceItemShuttle // - DOSHUT - UpdateMTOServiceItemShuttle // - IDSHUT - UpdateMTOServiceItemInternationalShuttle diff --git a/pkg/gen/primev3messages/update_m_t_o_service_item_s_i_t.go b/pkg/gen/primev3messages/update_m_t_o_service_item_s_i_t.go index 7012106e91e..ea432a5d501 100644 --- a/pkg/gen/primev3messages/update_m_t_o_service_item_s_i_t.go +++ b/pkg/gen/primev3messages/update_m_t_o_service_item_s_i_t.go @@ -39,7 +39,7 @@ type UpdateMTOServiceItemSIT struct { FirstAvailableDeliveryDate2 *strfmt.Date `json:"firstAvailableDeliveryDate2,omitempty"` // Service code allowed for this model type. - // Enum: [DDDSIT DOPSIT DOASIT DOFSIT] + // Enum: [DDDSIT DOPSIT DOASIT DOFSIT IDDSIT IOPSIT IOASIT IOFSIT] ReServiceCode string `json:"reServiceCode,omitempty"` // Indicates if "Approvals Requested" status is being requested. @@ -123,7 +123,7 @@ func (m *UpdateMTOServiceItemSIT) UnmarshalJSON(raw []byte) error { FirstAvailableDeliveryDate2 *strfmt.Date `json:"firstAvailableDeliveryDate2,omitempty"` // Service code allowed for this model type. - // Enum: [DDDSIT DOPSIT DOASIT DOFSIT] + // Enum: [DDDSIT DOPSIT DOASIT DOFSIT IDDSIT IOPSIT IOASIT IOFSIT] ReServiceCode string `json:"reServiceCode,omitempty"` // Indicates if "Approvals Requested" status is being requested. @@ -242,7 +242,7 @@ func (m UpdateMTOServiceItemSIT) MarshalJSON() ([]byte, error) { FirstAvailableDeliveryDate2 *strfmt.Date `json:"firstAvailableDeliveryDate2,omitempty"` // Service code allowed for this model type. - // Enum: [DDDSIT DOPSIT DOASIT DOFSIT] + // Enum: [DDDSIT DOPSIT DOASIT DOFSIT IDDSIT IOPSIT IOASIT IOFSIT] ReServiceCode string `json:"reServiceCode,omitempty"` // Indicates if "Approvals Requested" status is being requested. @@ -471,7 +471,7 @@ var updateMTOServiceItemSITTypeReServiceCodePropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["DDDSIT","DOPSIT","DOASIT","DOFSIT"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["DDDSIT","DOPSIT","DOASIT","DOFSIT","IDDSIT","IOPSIT","IOASIT","IOFSIT"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/services/mto_service_item/mto_service_item_creator.go b/pkg/services/mto_service_item/mto_service_item_creator.go index da03da22907..a001e3cef2a 100644 --- a/pkg/services/mto_service_item/mto_service_item_creator.go +++ b/pkg/services/mto_service_item/mto_service_item_creator.go @@ -601,11 +601,13 @@ func (o *mtoServiceItemCreator) CreateMTOServiceItem(appCtx appcontext.AppContex milesCalculated, errCalcSITDelivery := o.calculateSITDeliveryMiles(appCtx, serviceItem, mtoShipment) // only calculate SITDeliveryMiles for DOPSIT and DOSFSC origin service items - if serviceItem.ReService.Code == models.ReServiceCodeDOFSIT && milesCalculated != 0 { + if (serviceItem.ReService.Code == models.ReServiceCodeDOFSIT || serviceItem.ReService.Code == models.ReServiceCodeIOFSIT) && + milesCalculated != 0 { for itemIndex := range *extraServiceItems { extraServiceItem := &(*extraServiceItems)[itemIndex] - if extraServiceItem.ReService.Code == models.ReServiceCodeDOPSIT || - extraServiceItem.ReService.Code == models.ReServiceCodeDOSFSC { + if extraServiceItem.ReService.Code == models.ReServiceCodeDOPSIT || extraServiceItem.ReService.Code == models.ReServiceCodeIOPSIT || + extraServiceItem.ReService.Code == models.ReServiceCodeDOSFSC || + extraServiceItem.ReService.Code == models.ReServiceCodeIOSFSC { if milesCalculated > 0 && errCalcSITDelivery == nil { extraServiceItem.SITDeliveryMiles = &milesCalculated } @@ -614,11 +616,12 @@ func (o *mtoServiceItemCreator) CreateMTOServiceItem(appCtx appcontext.AppContex } // only calculate SITDeliveryMiles for DDDSIT and DDSFSC destination service items - if serviceItem.ReService.Code == models.ReServiceCodeDDFSIT && milesCalculated != 0 { + if (serviceItem.ReService.Code == models.ReServiceCodeDDFSIT || serviceItem.ReService.Code == models.ReServiceCodeIDFSIT) && milesCalculated != 0 { for itemIndex := range *extraServiceItems { extraServiceItem := &(*extraServiceItems)[itemIndex] - if extraServiceItem.ReService.Code == models.ReServiceCodeDDDSIT || - extraServiceItem.ReService.Code == models.ReServiceCodeDDSFSC { + if extraServiceItem.ReService.Code == models.ReServiceCodeDDDSIT || extraServiceItem.ReService.Code == models.ReServiceCodeIDDSIT || + extraServiceItem.ReService.Code == models.ReServiceCodeDDSFSC || + extraServiceItem.ReService.Code == models.ReServiceCodeIDSFSC { if milesCalculated > 0 && errCalcSITDelivery == nil { extraServiceItem.SITDeliveryMiles = &milesCalculated } diff --git a/swagger-def/prime.yaml b/swagger-def/prime.yaml index fc5a4f9217a..4b5b55df2cd 100644 --- a/swagger-def/prime.yaml +++ b/swagger-def/prime.yaml @@ -756,6 +756,82 @@ paths: **Addt'l days destination SIT service item**. This represents an additional day of storage for the same item. Additional DDASIT service items can be created and added to an existing shipment that **includes a DDFSIT service item**. + + --- + + **`MTOServiceItemInternationalOriginSIT`** + + MTOServiceItemInternationalOriginSIT is a subtype of MTOServiceItem. + + This model type describes a international origin SIT service item. Items can be created using this + model type with the following codes: + + **IOFSIT** + + **1st day origin SIT service item**. When a IOFSIT is requested, the API will auto-create the following group of service items: + * IOFSIT - International origin 1st day SIT + * IOASIT - International origin Additional day SIT + * IOPSIT - International origin SIT pickup + * IOSFSC - International origin SIT fuel surcharge + + **IOASIT** + + **Addt'l days origin SIT service item**. This represents an additional day of storage for the same item. + Additional IOASIT service items can be created and added to an existing shipment that **includes a IOFSIT service item**. + + --- + + **`MTOServiceItemInternationalDestSIT`** + + MTOServiceItemInternationalDestSIT is a subtype of MTOServiceItem. + + This model type describes a international destination SIT service item. Items can be created using this + model type with the following codes: + + **IDFSIT** + + **1st day destination SIT service item**. + + These additional fields are optional for creating a IDFSIT: + * `firstAvailableDeliveryDate1` + * string + * First available date that Prime can deliver SIT service item. + * firstAvailableDeliveryDate1, dateOfContact1, and timeMilitary1 are required together + * `dateOfContact1` + * string + * Date of attempted contact by the prime corresponding to `timeMilitary1` + * dateOfContact1, timeMilitary1, and firstAvailableDeliveryDate1 are required together + * `timeMilitary1` + * string\d{4}Z + * Time of attempted contact corresponding to `dateOfContact1`, in military format. + * timeMilitary1, dateOfContact1, and firstAvailableDeliveryDate1 are required together + * `firstAvailableDeliveryDate2` + * string + * Second available date that Prime can deliver SIT service item. + * firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 are required together + * `dateOfContact2` + * string + * Date of attempted contact delivery by the prime corresponding to `timeMilitary2` + * dateOfContact2, timeMilitary2, and firstAvailableDeliveryDate2 are required together + * `timeMilitary2` + * string\d{4}Z + * Time of attempted contact corresponding to `dateOfContact2`, in military format. + * timeMilitary2, dateOfContact2, and firstAvailableDeliveryDate2 are required together + + When a IDFSIT is requested, the API will auto-create the following group of service items: + * IDFSIT - International destination 1st day SIT + * IDASIT - International destination Additional day SIT + * IDDSIT - International destination SIT delivery + * IDSFSC - International destination SIT fuel surcharge + + **NOTE** When providing the `sitEntryDate` value in the payload, please ensure that the date is not BEFORE + `firstAvailableDeliveryDate1` or `firstAvailableDeliveryDate2`. If it is, you will receive an error response. + + **IDASIT** + + **Addt'l days destination SIT service item**. This represents an additional day of storage for the same item. + Additional IDASIT service items can be created and added to an existing shipment that **includes a IDFSIT service item**. + operationId: createMTOServiceItem tags: - mtoServiceItem @@ -822,6 +898,14 @@ paths: - DOFSIT - DDSFSC - DOSFSC + - IDASIT + - IDDSIT + - IDFSIT + - IOASIT + - IOPSIT + - IOFSIT + - IDSFSC + - IOSFSC The following Accessorial service items can be resubmitted following a rejection: - IOSHUT @@ -1762,6 +1846,8 @@ definitions: This should be populated for the following service items: * DOASIT(Domestic origin Additional day SIT) * DDASIT(Domestic destination Additional day SIT) + * IOASIT(International origin Additional day SIT) + * IDASIT(International destination Additional day SIT) Both take in the following param keys: * `SITPaymentRequestStart` diff --git a/swagger-def/prime_v2.yaml b/swagger-def/prime_v2.yaml index 9c6dbe33fd8..8563fdf576a 100644 --- a/swagger-def/prime_v2.yaml +++ b/swagger-def/prime_v2.yaml @@ -615,6 +615,10 @@ definitions: * DOPSIT - UpdateMTOServiceItemSIT * DOASIT - UpdateMTOServiceItemSIT * DOFSIT - UpdateMTOServiceItemSIT + * IDDSIT - UpdateMTOServiceItemSIT + * IOPSIT - UpdateMTOServiceItemSIT + * IOASIT - UpdateMTOServiceItemSIT + * IOFSIT - UpdateMTOServiceItemSIT * DDSHUT - UpdateMTOServiceItemShuttle * DOSHUT - UpdateMTOServiceItemShuttle * IDSHUT - UpdateMTOServiceItemInternationalShuttle @@ -918,6 +922,10 @@ definitions: - DOPSIT # Domestic Origin SIT Pickup - DOASIT # Domestic Origin Add'l Days SIT - DOFSIT # Domestic Origin 1st Day SIT + - IDDSIT # International Destination SIT Delivery + - IOPSIT # International Origin SIT Pickup + - IOASIT # International Origin Add'l Days SIT + - IOFSIT # International Origin 1st Day SIT sitDepartureDate: format: date type: string diff --git a/swagger-def/prime_v3.yaml b/swagger-def/prime_v3.yaml index 7f64d2718b3..14c9210b552 100644 --- a/swagger-def/prime_v3.yaml +++ b/swagger-def/prime_v3.yaml @@ -642,6 +642,10 @@ definitions: * DOPSIT - UpdateMTOServiceItemSIT * DOASIT - UpdateMTOServiceItemSIT * DOFSIT - UpdateMTOServiceItemSIT + * IDDSIT - UpdateMTOServiceItemSIT + * IOPSIT - UpdateMTOServiceItemSIT + * IOASIT - UpdateMTOServiceItemSIT + * IOFSIT - UpdateMTOServiceItemSIT * DDSHUT - UpdateMTOServiceItemShuttle * DOSHUT - UpdateMTOServiceItemShuttle * IDSHUT - UpdateMTOServiceItemInternationalShuttle @@ -1005,6 +1009,10 @@ definitions: - DOPSIT # Domestic Origin SIT Pickup - DOASIT # Domestic Origin Add'l Days SIT - DOFSIT # Domestic Origin 1st Day SIT + - IDDSIT # International Destination SIT Delivery + - IOPSIT # International Origin SIT Pickup + - IOASIT # International Origin Add'l Days SIT + - IOFSIT # International Origin 1st Day SIT sitDepartureDate: format: date type: string diff --git a/swagger/prime.yaml b/swagger/prime.yaml index c384de87904..595f8083eae 100644 --- a/swagger/prime.yaml +++ b/swagger/prime.yaml @@ -956,6 +956,112 @@ paths: Additional DDASIT service items can be created and added to an existing shipment that **includes a DDFSIT service item**. + + + --- + + + **`MTOServiceItemInternationalOriginSIT`** + + + MTOServiceItemInternationalOriginSIT is a subtype of MTOServiceItem. + + + This model type describes a international origin SIT service item. Items + can be created using this + + model type with the following codes: + + + **IOFSIT** + + + **1st day origin SIT service item**. When a IOFSIT is requested, the API + will auto-create the following group of service items: + * IOFSIT - International origin 1st day SIT + * IOASIT - International origin Additional day SIT + * IOPSIT - International origin SIT pickup + * IOSFSC - International origin SIT fuel surcharge + + **IOASIT** + + + **Addt'l days origin SIT service item**. This represents an additional + day of storage for the same item. + + Additional IOASIT service items can be created and added to an existing + shipment that **includes a IOFSIT service item**. + + + --- + + + **`MTOServiceItemInternationalDestSIT`** + + + MTOServiceItemInternationalDestSIT is a subtype of MTOServiceItem. + + + This model type describes a international destination SIT service item. + Items can be created using this + + model type with the following codes: + + + **IDFSIT** + + + **1st day destination SIT service item**. + + + These additional fields are optional for creating a IDFSIT: + * `firstAvailableDeliveryDate1` + * string + * First available date that Prime can deliver SIT service item. + * firstAvailableDeliveryDate1, dateOfContact1, and timeMilitary1 are required together + * `dateOfContact1` + * string + * Date of attempted contact by the prime corresponding to `timeMilitary1` + * dateOfContact1, timeMilitary1, and firstAvailableDeliveryDate1 are required together + * `timeMilitary1` + * string\d{4}Z + * Time of attempted contact corresponding to `dateOfContact1`, in military format. + * timeMilitary1, dateOfContact1, and firstAvailableDeliveryDate1 are required together + * `firstAvailableDeliveryDate2` + * string + * Second available date that Prime can deliver SIT service item. + * firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 are required together + * `dateOfContact2` + * string + * Date of attempted contact delivery by the prime corresponding to `timeMilitary2` + * dateOfContact2, timeMilitary2, and firstAvailableDeliveryDate2 are required together + * `timeMilitary2` + * string\d{4}Z + * Time of attempted contact corresponding to `dateOfContact2`, in military format. + * timeMilitary2, dateOfContact2, and firstAvailableDeliveryDate2 are required together + + When a IDFSIT is requested, the API will auto-create the following group + of service items: + * IDFSIT - International destination 1st day SIT + * IDASIT - International destination Additional day SIT + * IDDSIT - International destination SIT delivery + * IDSFSC - International destination SIT fuel surcharge + + **NOTE** When providing the `sitEntryDate` value in the payload, please + ensure that the date is not BEFORE + + `firstAvailableDeliveryDate1` or `firstAvailableDeliveryDate2`. If it + is, you will receive an error response. + + + **IDASIT** + + + **Addt'l days destination SIT service item**. This represents an + additional day of storage for the same item. + + Additional IDASIT service items can be created and added to an existing + shipment that **includes a IDFSIT service item**. operationId: createMTOServiceItem tags: - mtoServiceItem @@ -1059,6 +1165,22 @@ paths: - DOSFSC + - IDASIT + + - IDDSIT + + - IDFSIT + + - IOASIT + + - IOPSIT + + - IOFSIT + + - IDSFSC + + - IOSFSC + The following Accessorial service items can be resubmitted following a rejection: @@ -2782,6 +2904,8 @@ definitions: This should be populated for the following service items: * DOASIT(Domestic origin Additional day SIT) * DDASIT(Domestic destination Additional day SIT) + * IOASIT(International origin Additional day SIT) + * IDASIT(International destination Additional day SIT) Both take in the following param keys: * `SITPaymentRequestStart` diff --git a/swagger/prime_v2.yaml b/swagger/prime_v2.yaml index 4e4e1e298e2..e81f75fcbad 100644 --- a/swagger/prime_v2.yaml +++ b/swagger/prime_v2.yaml @@ -1168,6 +1168,10 @@ definitions: * DOPSIT - UpdateMTOServiceItemSIT * DOASIT - UpdateMTOServiceItemSIT * DOFSIT - UpdateMTOServiceItemSIT + * IDDSIT - UpdateMTOServiceItemSIT + * IOPSIT - UpdateMTOServiceItemSIT + * IOASIT - UpdateMTOServiceItemSIT + * IOFSIT - UpdateMTOServiceItemSIT * DDSHUT - UpdateMTOServiceItemShuttle * DOSHUT - UpdateMTOServiceItemShuttle * IDSHUT - UpdateMTOServiceItemInternationalShuttle @@ -1536,6 +1540,10 @@ definitions: - DOPSIT - DOASIT - DOFSIT + - IDDSIT + - IOPSIT + - IOASIT + - IOFSIT sitDepartureDate: format: date type: string diff --git a/swagger/prime_v3.yaml b/swagger/prime_v3.yaml index 938800d01f4..f622639afb0 100644 --- a/swagger/prime_v3.yaml +++ b/swagger/prime_v3.yaml @@ -1206,6 +1206,10 @@ definitions: * DOPSIT - UpdateMTOServiceItemSIT * DOASIT - UpdateMTOServiceItemSIT * DOFSIT - UpdateMTOServiceItemSIT + * IDDSIT - UpdateMTOServiceItemSIT + * IOPSIT - UpdateMTOServiceItemSIT + * IOASIT - UpdateMTOServiceItemSIT + * IOFSIT - UpdateMTOServiceItemSIT * DDSHUT - UpdateMTOServiceItemShuttle * DOSHUT - UpdateMTOServiceItemShuttle * IDSHUT - UpdateMTOServiceItemInternationalShuttle @@ -1643,6 +1647,10 @@ definitions: - DOPSIT - DOASIT - DOFSIT + - IDDSIT + - IOPSIT + - IOASIT + - IOFSIT sitDepartureDate: format: date type: string From 8b6b2c2dee38239b369def41ae6a8448311cf7ce Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Tue, 28 Jan 2025 20:00:39 +0000 Subject: [PATCH 04/26] add more tests --- .../primeapi/mto_service_item_test.go | 81 ++++- .../payloads/payload_to_model_test.go | 64 ++++ .../payloads/payload_to_model_test.go | 64 ++++ .../mto_service_item_validators_test.go | 307 ++++++++++++++++++ .../sit_entry_date_updater_test.go | 76 +++++ 5 files changed, 580 insertions(+), 12 deletions(-) diff --git a/pkg/handlers/primeapi/mto_service_item_test.go b/pkg/handlers/primeapi/mto_service_item_test.go index ceae1b2bf65..85525fc9824 100644 --- a/pkg/handlers/primeapi/mto_service_item_test.go +++ b/pkg/handlers/primeapi/mto_service_item_test.go @@ -43,7 +43,7 @@ func (suite *HandlerSuite) TestCreateMTOServiceItemHandler() { mtoServiceItem models.MTOServiceItem } - makeSubtestDataWithPPMShipmentType := func(isPPM bool) (subtestData *localSubtestData) { + makeSubtestDataWithPPMShipmentType := func(isPPM bool, isInternational bool) (subtestData *localSubtestData) { subtestData = &localSubtestData{} mtoShipmentID, _ := uuid.NewV4() @@ -62,15 +62,34 @@ func (suite *HandlerSuite) TestCreateMTOServiceItemHandler() { }, }, nil) } else { - subtestData.mtoShipment = factory.BuildMTOShipment(suite.DB(), []factory.Customization{ - { - Model: mto, - LinkOnly: true, - }, - }, nil) + if isInternational { + subtestData.mtoShipment = factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: mto, + LinkOnly: true, + }, + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeInternational, + }, + }, + }, nil) + } else { + subtestData.mtoShipment = factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: mto, + LinkOnly: true, + }, + }, nil) + } + } + + if isInternational { + factory.FetchReServiceByCode(suite.DB(), models.ReServiceCodeIOFSIT) + } else { + factory.FetchReServiceByCode(suite.DB(), models.ReServiceCodeDOFSIT) } - factory.FetchReServiceByCode(suite.DB(), models.ReServiceCodeDOFSIT) req := httptest.NewRequest("POST", "/mto-service-items", nil) sitEntryDate := time.Now() sitPostalCode := "00000" @@ -84,10 +103,16 @@ func (suite *HandlerSuite) TestCreateMTOServiceItemHandler() { // that we properly create the address coming in from the API. actualPickupAddress := factory.BuildAddress(nil, nil, []factory.Trait{factory.GetTraitAddress2}) + serviceCode := models.ReService{Code: models.ReServiceCodeDOFSIT} + + if isInternational { + serviceCode = models.ReService{Code: models.ReServiceCodeIOFSIT} + } + subtestData.mtoServiceItem = models.MTOServiceItem{ MoveTaskOrderID: mto.ID, MTOShipmentID: &subtestData.mtoShipment.ID, - ReService: models.ReService{Code: models.ReServiceCodeDOFSIT}, + ReService: serviceCode, Reason: models.StringPointer("lorem ipsum"), SITEntryDate: &sitEntryDate, SITPostalCode: &sitPostalCode, @@ -104,7 +129,11 @@ func (suite *HandlerSuite) TestCreateMTOServiceItemHandler() { } makeSubtestData := func() (subtestData *localSubtestData) { - return makeSubtestDataWithPPMShipmentType(false) + return makeSubtestDataWithPPMShipmentType(false, false) + } + + makeSubtestInternationalData := func() (subtestData *localSubtestData) { + return makeSubtestDataWithPPMShipmentType(false, true) } suite.Run("Successful POST - Integration Test", func() { @@ -144,6 +173,34 @@ func (suite *HandlerSuite) TestCreateMTOServiceItemHandler() { suite.NotZero(okResponse.Payload[0].ID()) }) + suite.Run("Successful POST International - Integration Test", func() { + subtestData := makeSubtestInternationalData() + moveRouter := moverouter.NewMoveRouter() + planner := &routemocks.Planner{} + planner.On("ZipTransitDistance", + mock.AnythingOfType("*appcontext.appContext"), + mock.Anything, + mock.Anything, + false, + false, + ).Return(400, nil) + creator := mtoserviceitem.NewMTOServiceItemCreator(planner, builder, moveRouter, ghcrateengine.NewDomesticUnpackPricer(), ghcrateengine.NewDomesticPackPricer(), ghcrateengine.NewDomesticLinehaulPricer(), ghcrateengine.NewDomesticShorthaulPricer(), ghcrateengine.NewDomesticOriginPricer(), ghcrateengine.NewDomesticDestinationPricer(), ghcrateengine.NewFuelSurchargePricer()) + handler := CreateMTOServiceItemHandler{ + suite.HandlerConfig(), + creator, + mtoChecker, + } + + // Validate incoming payload + suite.NoError(subtestData.params.Body.Validate(strfmt.Default)) + + response := handler.Handle(subtestData.params) + suite.IsType(&mtoserviceitemops.CreateMTOServiceItemOK{}, response) + okResponse := response.(*mtoserviceitemops.CreateMTOServiceItemOK) + + suite.NotZero(okResponse.Payload[0].ID()) + }) + suite.Run("Successful POST for Creating Shuttling without PrimeEstimatedWeight set - Integration Test", func() { mto := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) mtoShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ @@ -506,7 +563,7 @@ func (suite *HandlerSuite) TestCreateMTOServiceItemHandler() { }) suite.Run("POST failure - Shipment fetch not found", func() { - subtestData := makeSubtestDataWithPPMShipmentType(true) + subtestData := makeSubtestDataWithPPMShipmentType(true, false) moveRouter := moverouter.NewMoveRouter() planner := &routemocks.Planner{} planner.On("ZipTransitDistance", @@ -538,7 +595,7 @@ func (suite *HandlerSuite) TestCreateMTOServiceItemHandler() { }) suite.Run("POST failure - 422 - PPM not allowed to create service item", func() { - subtestData := makeSubtestDataWithPPMShipmentType(true) + subtestData := makeSubtestDataWithPPMShipmentType(true, false) moveRouter := moverouter.NewMoveRouter() planner := &routemocks.Planner{} planner.On("ZipTransitDistance", diff --git a/pkg/handlers/primeapiv2/payloads/payload_to_model_test.go b/pkg/handlers/primeapiv2/payloads/payload_to_model_test.go index 3df180b58ea..194f4211f1c 100644 --- a/pkg/handlers/primeapiv2/payloads/payload_to_model_test.go +++ b/pkg/handlers/primeapiv2/payloads/payload_to_model_test.go @@ -378,6 +378,32 @@ func (suite *PayloadsSuite) TestMTOServiceItemModel() { suite.Equal(destUSPRCID.String(), returnedModel.SITDestinationFinalAddress.UsPostRegionCityID.String()) }) + suite.Run("Success - Returns SIT destination service item model - international", func() { + destSITServiceItem := &primev2messages.MTOServiceItemInternationalDestSIT{ + ReServiceCode: &destServiceCode, + FirstAvailableDeliveryDate1: &destDate, + FirstAvailableDeliveryDate2: &destDate, + DateOfContact1: &destDate, + DateOfContact2: &destDate, + TimeMilitary1: &destTime, + TimeMilitary2: &destTime, + SitDestinationFinalAddress: &sitFinalDestAddress, + Reason: &destReason, + } + + destSITServiceItem.SetMoveTaskOrderID(handlers.FmtUUID(moveTaskOrderIDField)) + destSITServiceItem.SetMtoShipmentID(*mtoShipmentIDString) + returnedModel, verrs := MTOServiceItemModel(destSITServiceItem) + + suite.NoVerrs(verrs) + suite.Equal(moveTaskOrderIDField.String(), returnedModel.MoveTaskOrderID.String()) + suite.Equal(mtoShipmentIDField.String(), returnedModel.MTOShipmentID.String()) + suite.Equal(models.ReServiceCodeDDFSIT, returnedModel.ReService.Code) + suite.Equal(destPostalCode, returnedModel.SITDestinationFinalAddress.PostalCode) + suite.Equal(destStreet, returnedModel.SITDestinationFinalAddress.StreetAddress1) + suite.Equal(destUSPRCID.String(), returnedModel.SITDestinationFinalAddress.UsPostRegionCityID.String()) + }) + suite.Run("Success - Returns SIT destination service item model without customer contact fields", func() { destSITServiceItem := &primev2messages.MTOServiceItemDestSIT{ ReServiceCode: &destServiceCode, @@ -398,6 +424,27 @@ func (suite *PayloadsSuite) TestMTOServiceItemModel() { suite.Equal(destUSPRCID.String(), returnedModel.SITDestinationFinalAddress.UsPostRegionCityID.String()) suite.Equal(destReason, *returnedModel.Reason) }) + + suite.Run("Success - Returns SIT destination service item model without customer contact fields - international", func() { + destSITServiceItem := &primev2messages.MTOServiceItemInternationalDestSIT{ + ReServiceCode: &destServiceCode, + SitDestinationFinalAddress: &sitFinalDestAddress, + Reason: &destReason, + } + + destSITServiceItem.SetMoveTaskOrderID(handlers.FmtUUID(moveTaskOrderIDField)) + destSITServiceItem.SetMtoShipmentID(*mtoShipmentIDString) + returnedModel, verrs := MTOServiceItemModel(destSITServiceItem) + + suite.NoVerrs(verrs) + suite.Equal(moveTaskOrderIDField.String(), returnedModel.MoveTaskOrderID.String()) + suite.Equal(mtoShipmentIDField.String(), returnedModel.MTOShipmentID.String()) + suite.Equal(models.ReServiceCodeDDFSIT, returnedModel.ReService.Code) + suite.Equal(destPostalCode, returnedModel.SITDestinationFinalAddress.PostalCode) + suite.Equal(destStreet, returnedModel.SITDestinationFinalAddress.StreetAddress1) + suite.Equal(destUSPRCID.String(), returnedModel.SITDestinationFinalAddress.UsPostRegionCityID.String()) + suite.Equal(destReason, *returnedModel.Reason) + }) } func (suite *PayloadsSuite) TestReweighModelFromUpdate() { @@ -641,6 +688,23 @@ func (suite *PayloadsSuite) TestValidateReasonOriginSIT() { verrs := validateReasonOriginSIT(mtoServiceItemOriginSIT) suite.True(verrs.HasAny()) }) + + suite.Run("Reason provided - international", func() { + reason := "reason" + mtoServiceItemOriginSIT := primev2messages.MTOServiceItemInternationalOriginSIT{ + Reason: &reason, + } + + verrs := validateReasonInternationalOriginSIT(mtoServiceItemOriginSIT) + suite.False(verrs.HasAny()) + }) + + suite.Run("No reason provided - international", func() { + mtoServiceItemOriginSIT := primev2messages.MTOServiceItemInternationalOriginSIT{} + + verrs := validateReasonInternationalOriginSIT(mtoServiceItemOriginSIT) + suite.True(verrs.HasAny()) + }) } func (suite *PayloadsSuite) TestShipmentAddressUpdateModel() { diff --git a/pkg/handlers/primeapiv3/payloads/payload_to_model_test.go b/pkg/handlers/primeapiv3/payloads/payload_to_model_test.go index fd9430379f0..1697fe78edd 100644 --- a/pkg/handlers/primeapiv3/payloads/payload_to_model_test.go +++ b/pkg/handlers/primeapiv3/payloads/payload_to_model_test.go @@ -378,6 +378,32 @@ func (suite *PayloadsSuite) TestMTOServiceItemModel() { suite.Equal(destUSPRCID.String(), returnedModel.SITDestinationFinalAddress.UsPostRegionCityID.String()) }) + suite.Run("Success - Returns international SIT destination service item model", func() { + destSITServiceItem := &primev3messages.MTOServiceItemInternationalDestSIT{ + ReServiceCode: &destServiceCode, + FirstAvailableDeliveryDate1: &destDate, + FirstAvailableDeliveryDate2: &destDate, + DateOfContact1: &destDate, + DateOfContact2: &destDate, + TimeMilitary1: &destTime, + TimeMilitary2: &destTime, + SitDestinationFinalAddress: &sitFinalDestAddress, + Reason: &destReason, + } + + destSITServiceItem.SetMoveTaskOrderID(handlers.FmtUUID(moveTaskOrderIDField)) + destSITServiceItem.SetMtoShipmentID(*mtoShipmentIDString) + returnedModel, verrs := MTOServiceItemModel(destSITServiceItem) + + suite.NoVerrs(verrs) + suite.Equal(moveTaskOrderIDField.String(), returnedModel.MoveTaskOrderID.String()) + suite.Equal(mtoShipmentIDField.String(), returnedModel.MTOShipmentID.String()) + suite.Equal(models.ReServiceCodeDDFSIT, returnedModel.ReService.Code) + suite.Equal(destPostalCode, returnedModel.SITDestinationFinalAddress.PostalCode) + suite.Equal(destStreet, returnedModel.SITDestinationFinalAddress.StreetAddress1) + suite.Equal(destUSPRCID.String(), returnedModel.SITDestinationFinalAddress.UsPostRegionCityID.String()) + }) + suite.Run("Success - Returns SIT destination service item model without customer contact fields", func() { destSITServiceItem := &primev3messages.MTOServiceItemDestSIT{ ReServiceCode: &destServiceCode, @@ -398,6 +424,27 @@ func (suite *PayloadsSuite) TestMTOServiceItemModel() { suite.Equal(destUSPRCID.String(), returnedModel.SITDestinationFinalAddress.UsPostRegionCityID.String()) suite.Equal(destReason, *returnedModel.Reason) }) + + suite.Run("Success - Returns international SIT destination service item model without customer contact fields", func() { + destSITServiceItem := &primev3messages.MTOServiceItemInternationalDestSIT{ + ReServiceCode: &destServiceCode, + SitDestinationFinalAddress: &sitFinalDestAddress, + Reason: &destReason, + } + + destSITServiceItem.SetMoveTaskOrderID(handlers.FmtUUID(moveTaskOrderIDField)) + destSITServiceItem.SetMtoShipmentID(*mtoShipmentIDString) + returnedModel, verrs := MTOServiceItemModel(destSITServiceItem) + + suite.NoVerrs(verrs) + suite.Equal(moveTaskOrderIDField.String(), returnedModel.MoveTaskOrderID.String()) + suite.Equal(mtoShipmentIDField.String(), returnedModel.MTOShipmentID.String()) + suite.Equal(models.ReServiceCodeDDFSIT, returnedModel.ReService.Code) + suite.Equal(destPostalCode, returnedModel.SITDestinationFinalAddress.PostalCode) + suite.Equal(destStreet, returnedModel.SITDestinationFinalAddress.StreetAddress1) + suite.Equal(destUSPRCID.String(), returnedModel.SITDestinationFinalAddress.UsPostRegionCityID.String()) + suite.Equal(destReason, *returnedModel.Reason) + }) } func (suite *PayloadsSuite) TestReweighModelFromUpdate() { @@ -724,6 +771,23 @@ func (suite *PayloadsSuite) TestValidateReasonOriginSIT() { verrs := validateReasonOriginSIT(mtoServiceItemOriginSIT) suite.True(verrs.HasAny()) }) + + suite.Run("Reason provided - international", func() { + reason := "reason" + mtoServiceItemOriginSIT := primev3messages.MTOServiceItemInternationalOriginSIT{ + Reason: &reason, + } + + verrs := validateReasonInternationalOriginSIT(mtoServiceItemOriginSIT) + suite.False(verrs.HasAny()) + }) + + suite.Run("No reason provided - international", func() { + mtoServiceItemOriginSIT := primev3messages.MTOServiceItemInternationalOriginSIT{} + + verrs := validateReasonInternationalOriginSIT(mtoServiceItemOriginSIT) + suite.True(verrs.HasAny()) + }) } func (suite *PayloadsSuite) TestShipmentAddressUpdateModel() { diff --git a/pkg/services/mto_service_item/mto_service_item_validators_test.go b/pkg/services/mto_service_item/mto_service_item_validators_test.go index 888c094becd..e8932bee2ee 100644 --- a/pkg/services/mto_service_item/mto_service_item_validators_test.go +++ b/pkg/services/mto_service_item/mto_service_item_validators_test.go @@ -181,6 +181,29 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemData() { suite.NoError(err) }) + suite.Run("checkForSITItemChanges - should not throw error when SIT Item is changed - international", func() { + + // Update the non-updateable fields: + oldServiceItem, newServiceItem := setupTestData() // Create old and new service item + + // Make both sthe newServiceItem of type DOFSIT because this type of service item will be checked by checkForSITItemChanges + newServiceItem.ReService.Code = models.ReServiceCodeIOFSIT + + // Sit Entry Date change. Need to make the newServiceItem different than the old. + newSitEntryDate := time.Date(2023, time.October, 10, 10, 10, 0, 0, time.UTC) + newServiceItem.SITEntryDate = &newSitEntryDate + + serviceItemData := updateMTOServiceItemData{ + updatedServiceItem: newServiceItem, + oldServiceItem: oldServiceItem, + verrs: validate.NewErrors(), + } + + err := serviceItemData.checkForSITItemChanges(&serviceItemData) + + suite.NoError(err) + }) + suite.Run("checkForSITItemChanges - should throw error when SIT Item is not changed", func() { oldServiceItem, newServiceItem := setupTestData() // Create old and new service item @@ -204,6 +227,29 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemData() { }) + suite.Run("checkForSITItemChanges - should throw error when SIT Item is not changed - international", func() { + + oldServiceItem, newServiceItem := setupTestData() // Create old and new service item + + // Make both service items of type DOFSIT because this type of service item will be checked by checkForSITItemChanges + oldServiceItem.ReService.Code = models.ReServiceCodeIOFSIT + newServiceItem.ReService.Code = models.ReServiceCodeIOFSIT + oldServiceItem.SITDepartureDate, newServiceItem.SITDepartureDate = &now, &now + + serviceItemData := updateMTOServiceItemData{ + updatedServiceItem: newServiceItem, + oldServiceItem: oldServiceItem, + verrs: validate.NewErrors(), + } + + err := serviceItemData.checkForSITItemChanges(&serviceItemData) + + // Should error with message if nothing has changed between the new service item and the old one + suite.Error(err) + suite.Contains(err.Error(), "To re-submit a SIT sevice item the new SIT service item must be different than the previous one.") + + }) + // Test successful check for SIT departure service item - not updating SITDepartureDate suite.Run("checkSITDeparture w/ no SITDepartureDate update - success", func() { oldServiceItem, newServiceItem := setupTestData() // These @@ -247,6 +293,62 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemData() { suite.NoVerrs(serviceItemData.verrs) }) + // Test successful check for SIT departure service item - IDDSIT + suite.Run("checkSITDeparture w/ IDDSIT - success", func() { + // Under test: checkSITDeparture checks that the service item is a + // DDDSIT or DOPSIT if the user is trying to update the + // SITDepartureDate + // Set up: Create an old and new DDDSIT, with a new date and try to update. + // Expected outcome: Success if both are DDDSIT + oldDDDSIT := factory.BuildMTOServiceItem(nil, []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeIDDSIT, + }, + }, + }, nil) + newDDDSIT := oldDDDSIT + newDDDSIT.SITDepartureDate = &now + + serviceItemData := updateMTOServiceItemData{ + updatedServiceItem: newDDDSIT, + oldServiceItem: oldDDDSIT, + verrs: validate.NewErrors(), + } + err := serviceItemData.checkSITDeparture(suite.AppContextForTest()) + + suite.NoError(err) + suite.NoVerrs(serviceItemData.verrs) + }) + + // Test successful check for SIT departure service item - DDDSIT + suite.Run("checkSITDeparture w/ IDDSIT - success", func() { + // Under test: checkSITDeparture checks that the service item is a + // IDDSIT or IOPSIT if the user is trying to update the + // SITDepartureDate + // Set up: Create an old and new IDDSIT, with a new date and try to update. + // Expected outcome: Success if both are IDDSIT + oldIDDSIT := factory.BuildMTOServiceItem(nil, []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeIDDSIT, + }, + }, + }, nil) + newIDDSIT := oldIDDSIT + newIDDSIT.SITDepartureDate = &now + + serviceItemData := updateMTOServiceItemData{ + updatedServiceItem: newIDDSIT, + oldServiceItem: oldIDDSIT, + verrs: validate.NewErrors(), + } + err := serviceItemData.checkSITDeparture(suite.AppContextForTest()) + + suite.NoError(err) + suite.NoVerrs(serviceItemData.verrs) + }) + // Test unsuccessful check for SIT departure service item - not a departure SIT item suite.Run("checkSITDeparture w/ non-departure SIT - failure", func() { // Under test: checkSITDeparture checks that the service item is a @@ -397,6 +499,37 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemData() { suite.Contains(err.Error(), "- reason cannot be empty when resubmitting a previously rejected SIT service item") }) + // Test unsuccessful check service item when the reason isn't being updated + suite.Run("checkReasonWasUpdatedOnRejectedSIT - failure when empty string - international", func() { + // Under test: checkReasonWasUpdatedOnRejectedSIT ensures that the reason value is being updated + // Set up: Create any SIT service item + // Expected outcome: ConflictError + oldServiceItem, newServiceItem := setupTestData() + + // only checks rejected SIT service items + newServiceItem.Status = models.MTOServiceItemStatusSubmitted + oldServiceItem.Status = models.MTOServiceItemStatusRejected + + // This only checks SIT service items + newServiceItem.ReService.Code = models.ReServiceCodeIDFSIT + oldServiceItem.ReService.Code = models.ReServiceCodeIDFSIT + + newServiceItem.Reason = models.StringPointer("") + oldServiceItem.Reason = models.StringPointer("a reason") + + serviceItemData := updateMTOServiceItemData{ + updatedServiceItem: newServiceItem, + oldServiceItem: oldServiceItem, + verrs: validate.NewErrors(), + } + err := serviceItemData.checkReasonWasUpdatedOnRejectedSIT(suite.AppContextForTest()) + + suite.Error(err) + suite.IsType(apperror.ConflictError{}, err) + suite.NoVerrs(serviceItemData.verrs) + suite.Contains(err.Error(), "- reason cannot be empty when resubmitting a previously rejected SIT service item") + }) + // Test unsuccessful check service item when the reason isn't being updated suite.Run("checkReasonWasUpdatedOnRejectedSIT - failure when no reason is provided", func() { // Under test: checkReasonWasUpdatedOnRejectedSIT ensures that the reason value is being updated @@ -428,6 +561,37 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemData() { suite.Contains(err.Error(), "- you must provide a new reason when resubmitting a previously rejected SIT service item") }) + // Test unsuccessful check service item when the reason isn't being updated + suite.Run("checkReasonWasUpdatedOnRejectedSIT - failure when no reason is provided - international", func() { + // Under test: checkReasonWasUpdatedOnRejectedSIT ensures that the reason value is being updated + // Set up: Create any SIT service item + // Expected outcome: ConflictError + oldServiceItem, newServiceItem := setupTestData() + + // only checks rejected SIT service items + newServiceItem.Status = models.MTOServiceItemStatusSubmitted + oldServiceItem.Status = models.MTOServiceItemStatusRejected + + // This only checks SIT service items + newServiceItem.ReService.Code = models.ReServiceCodeIDFSIT + oldServiceItem.ReService.Code = models.ReServiceCodeIDFSIT + + newServiceItem.Reason = nil + oldServiceItem.Reason = models.StringPointer("a reason") + + serviceItemData := updateMTOServiceItemData{ + updatedServiceItem: newServiceItem, + oldServiceItem: oldServiceItem, + verrs: validate.NewErrors(), + } + err := serviceItemData.checkReasonWasUpdatedOnRejectedSIT(suite.AppContextForTest()) + + suite.Error(err) + suite.IsType(apperror.ConflictError{}, err) + suite.NoVerrs(serviceItemData.verrs) + suite.Contains(err.Error(), "- you must provide a new reason when resubmitting a previously rejected SIT service item") + }) + suite.Run("checkReasonWasUpdatedOnRejectedSIT - success", func() { // Under test: checkReasonWasUpdatedOnRejectedSIT ensures that the reason value is being updated // Set up: Create any SIT service item @@ -456,6 +620,34 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemData() { suite.NoVerrs(serviceItemData.verrs) }) + suite.Run("checkReasonWasUpdatedOnRejectedSIT - international - success", func() { + // Under test: checkReasonWasUpdatedOnRejectedSIT ensures that the reason value is being updated + // Set up: Create any SIT service item + // Expected outcome: No errors + oldServiceItem, newServiceItem := setupTestData() + + // only checks rejected SIT service items + newServiceItem.Status = models.MTOServiceItemStatusSubmitted + oldServiceItem.Status = models.MTOServiceItemStatusRejected + + // This only checks SIT service items + newServiceItem.ReService.Code = models.ReServiceCodeIDFSIT + oldServiceItem.ReService.Code = models.ReServiceCodeIDFSIT + + newServiceItem.Reason = models.StringPointer("one reason") + oldServiceItem.Reason = models.StringPointer("another reason") + + serviceItemData := updateMTOServiceItemData{ + updatedServiceItem: newServiceItem, + oldServiceItem: oldServiceItem, + verrs: validate.NewErrors(), + } + err := serviceItemData.checkReasonWasUpdatedOnRejectedSIT(suite.AppContextForTest()) + + suite.NoError(err) + suite.NoVerrs(serviceItemData.verrs) + }) + // Test getVerrs for successful example suite.Run("getVerrs - success", func() { // Under test: getVerrs returns a list of validation errors @@ -858,6 +1050,72 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemData() { }) + suite.Run("SITDepartureDate - Does not error or update shipment auth end date when set after the authorized end date - international", func() { + // Under test: checkSITDepartureDate checks that + // the SITDepartureDate is not later than the authorized end date + // Set up: Create an old and new IOPSIT and IDDSIT, with a date later than the + // shipment and try to update. + // Expected outcome: No ERROR if departure date comes after the end date. + // Shipment auth end date does not change + mtoShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{OriginSITAuthEndDate: &now, + DestinationSITAuthEndDate: &now}, + }, + }, nil) + testCases := []struct { + reServiceCode models.ReServiceCode + }{ + { + reServiceCode: models.ReServiceCodeIOPSIT, + }, + { + reServiceCode: models.ReServiceCodeIDDSIT, + }, + } + for _, tc := range testCases { + oldSITServiceItem := factory.BuildMTOServiceItem(nil, []factory.Customization{ + { + Model: models.ReService{ + Code: tc.reServiceCode, + }, + }, + { + Model: mtoShipment, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + SITEntryDate: &later, + }, + }, + }, nil) + newSITServiceItem := oldSITServiceItem + newSITServiceItem.SITDepartureDate = &later + serviceItemData := updateMTOServiceItemData{ + updatedServiceItem: newSITServiceItem, + oldServiceItem: oldSITServiceItem, + verrs: validate.NewErrors(), + } + err := serviceItemData.checkSITDepartureDate(suite.AppContextForTest()) + suite.NoError(err) + suite.False(serviceItemData.verrs.HasAny()) + + // Double check the shipment and ensure that the SITDepartureDate is in fact after the authorized end date + var postUpdateShipment models.MTOShipment + err = suite.DB().Find(&postUpdateShipment, mtoShipment.ID) + suite.NoError(err) + if tc.reServiceCode == models.ReServiceCodeIOPSIT { + suite.True(mtoShipment.OriginSITAuthEndDate.Truncate(24 * time.Hour).Equal(postUpdateShipment.OriginSITAuthEndDate.Truncate(24 * time.Hour))) + suite.True(newSITServiceItem.SITEntryDate.Truncate(24 * time.Hour).After(postUpdateShipment.OriginSITAuthEndDate.Truncate(24 * time.Hour))) + } + if tc.reServiceCode == models.ReServiceCodeIDDSIT { + suite.True(mtoShipment.DestinationSITAuthEndDate.Truncate(24 * time.Hour).Equal(postUpdateShipment.DestinationSITAuthEndDate.Truncate(24 * time.Hour))) + suite.True(newSITServiceItem.SITEntryDate.Truncate(24 * time.Hour).After(postUpdateShipment.DestinationSITAuthEndDate.Truncate(24 * time.Hour))) + } + } + }) + suite.Run("SITDepartureDate - errors when set before the SIT entry date", func() { mtoShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ { @@ -908,6 +1166,55 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemData() { }) + suite.Run("SITDepartureDate - errors when set before the SIT entry date - international", func() { + mtoShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{OriginSITAuthEndDate: &now, + DestinationSITAuthEndDate: &now}, + }, + }, nil) + testCases := []struct { + reServiceCode models.ReServiceCode + }{ + { + reServiceCode: models.ReServiceCodeIOPSIT, + }, + { + reServiceCode: models.ReServiceCodeIDDSIT, + }, + } + for _, tc := range testCases { + oldSITServiceItem := factory.BuildMTOServiceItem(nil, []factory.Customization{ + { + Model: models.ReService{ + Code: tc.reServiceCode, + }, + }, + { + Model: mtoShipment, + LinkOnly: true, + }, + { + Model: models.MTOServiceItem{ + SITEntryDate: &later, + }, + }, + }, nil) + newSITServiceItem := oldSITServiceItem + newSITServiceItem.SITDepartureDate = &before + serviceItemData := updateMTOServiceItemData{ + updatedServiceItem: newSITServiceItem, + oldServiceItem: oldSITServiceItem, + verrs: validate.NewErrors(), + } + err := serviceItemData.checkSITDepartureDate(suite.AppContextForTest()) + suite.NoError(err) // Just verrs + suite.True(serviceItemData.verrs.HasAny()) + suite.Contains(serviceItemData.verrs.Keys(), "SITDepartureDate") + suite.Contains(serviceItemData.verrs.Get("SITDepartureDate"), "SIT departure date cannot be set before the SIT entry date.") + } + }) + suite.Run("SITDepartureDate - errors when service item is missing a shipment ID", func() { oldSITServiceItem := factory.BuildMTOServiceItem(nil, []factory.Customization{ diff --git a/pkg/services/sit_entry_date_update/sit_entry_date_updater_test.go b/pkg/services/sit_entry_date_update/sit_entry_date_updater_test.go index a6f45b1dcdc..4f11deab45d 100644 --- a/pkg/services/sit_entry_date_update/sit_entry_date_updater_test.go +++ b/pkg/services/sit_entry_date_update/sit_entry_date_updater_test.go @@ -48,6 +48,44 @@ func (suite *UpdateSitEntryDateServiceSuite) TestUpdateSitEntryDate() { return ddfServiceItem, ddaServiceItem } + setupInternationalModels := func() (models.MTOServiceItem, models.MTOServiceItem) { + move := factory.BuildMove(suite.DB(), nil, nil) + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeInternational, + }, + }, + }, nil) + idfServiceItem := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: shipment, + LinkOnly: true, + }, + { + Model: models.ReService{ + Code: models.ReServiceCodeIDFSIT, + }, + }, + }, nil) + idaServiceItem := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: shipment, + LinkOnly: true, + }, + { + Model: models.ReService{ + Code: models.ReServiceCodeIDASIT, + }, + }, + }, nil) + return idfServiceItem, idaServiceItem + } + // Test not found error suite.Run("Not Found Error", func() { ddfServiceItem, _ := setupModels() @@ -66,6 +104,23 @@ func (suite *UpdateSitEntryDateServiceSuite) TestUpdateSitEntryDate() { suite.IsType(apperror.NotFoundError{}, err) }) + suite.Run("Not Found Error - international", func() { + ddfServiceItem, _ := setupInternationalModels() + notFoundServiceItem := models.SITEntryDateUpdate{ + ID: ddfServiceItem.ID, + SITEntryDate: ddfServiceItem.SITEntryDate, + } + notFoundUUID, err := uuid.NewV4() + suite.NoError(err) + notFoundServiceItem.ID = notFoundUUID + + updatedServiceItem, err := updater.UpdateSitEntryDate(suite.AppContextForTest(), ¬FoundServiceItem) + + suite.Nil(updatedServiceItem) + suite.Error(err) + suite.IsType(apperror.NotFoundError{}, err) + }) + // Test successful update of both service items suite.Run("Successful update of service items", func() { ddfServiceItem, ddaServiceItem := setupModels() @@ -88,4 +143,25 @@ func (suite *UpdateSitEntryDateServiceSuite) TestUpdateSitEntryDate() { suite.Equal(ddaServiceItem.SITEntryDate.Local(), newSitEntryDateNextDay.Local()) }) + suite.Run("Successful update of service items - international", func() { + ddfServiceItem, ddaServiceItem := setupInternationalModels() + updatedServiceItem := models.SITEntryDateUpdate{ + ID: ddfServiceItem.ID, + SITEntryDate: ddfServiceItem.SITEntryDate, + } + newSitEntryDate := time.Date(2020, time.December, 02, 0, 0, 0, 0, time.UTC) + newSitEntryDateNextDay := newSitEntryDate.Add(24 * time.Hour) + + updatedServiceItem.SITEntryDate = &newSitEntryDate + ddaServiceItem.SITEntryDate = &newSitEntryDateNextDay + + changedServiceItem, err := updater.UpdateSitEntryDate(suite.AppContextForTest(), &updatedServiceItem) + + suite.NoError(err) + suite.NotNil(updatedServiceItem) + suite.Equal(ddfServiceItem.ID, updatedServiceItem.ID) + suite.Equal(updatedServiceItem.SITEntryDate.Local(), changedServiceItem.SITEntryDate.Local()) + suite.Equal(ddaServiceItem.SITEntryDate.Local(), newSitEntryDateNextDay.Local()) + }) + } From 8e104c0740e2d30930de8eb169baccdf17a693ec Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Tue, 28 Jan 2025 16:14:39 -0500 Subject: [PATCH 05/26] create separate create forms for international SITs --- .../CreateShipmentServiceItemForm.jsx | 26 +-- .../CreateShipmentServiceItemForm.test.jsx | 2 + .../DestinationSITServiceItemForm.jsx | 31 +--- .../DestinationSITServiceItemForm.test.jsx | 57 ------- ...rnationalDestinationSITServiceItemForm.jsx | 133 +++++++++++++++ ...onalDestinationSITServiceItemForm.test.jsx | 156 ++++++++++++++++++ .../InternationalOriginSITServiceItemForm.jsx | 110 ++++++++++++ ...rnationalOriginSITServiceItemForm.test.jsx | 96 +++++++++++ .../OriginSITServiceItemForm.jsx | 11 +- 9 files changed, 513 insertions(+), 109 deletions(-) create mode 100644 src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.jsx create mode 100644 src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.test.jsx create mode 100644 src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.jsx create mode 100644 src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.test.jsx diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.jsx index c5b4a3a0f19..6dc1c96593b 100644 --- a/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.jsx +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.jsx @@ -5,6 +5,8 @@ import PropTypes from 'prop-types'; import styles from './CreateShipmentServiceItemForm.module.scss'; import DestinationSITServiceItemForm from './DestinationSITServiceItemForm'; import OriginSITServiceItemForm from './OriginSITServiceItemForm'; +import InternationalDestinationSITServiceItemForm from './InternationalDestinationSITServiceItemForm'; +import InternationalOriginSITServiceItemForm from './InternationalOriginSITServiceItemForm'; import ShuttleSITServiceItemForm from './ShuttleSITServiceItemForm'; import DomesticCratingForm from './DomesticCratingForm'; import InternationalCratingForm from './InternationalCratingForm'; @@ -60,33 +62,17 @@ const CreateShipmentServiceItemForm = ({ shipment, createServiceItemMutation }) {selectedServiceItemType === MTOServiceItemOriginSIT && ( - + )} {selectedServiceItemType === MTOServiceItemDestSIT && ( - + )} {selectedServiceItemType === MTOServiceItemInternationalOriginSIT && ( - + )} {selectedServiceItemType === MTOServiceItemInternationalDestSIT && ( - + )} {selectedServiceItemType === MTOServiceItemShuttle && ( diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.test.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.test.jsx index f30c0b9f459..43868b6257c 100644 --- a/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.test.jsx +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/CreateShipmentServiceItemForm.test.jsx @@ -88,6 +88,8 @@ describe('CreateShipmentServiceItemForm component', () => { it.each([ ['originSITServiceItemForm', createServiceItemModelTypes.MTOServiceItemOriginSIT], ['destinationSITServiceItemForm', createServiceItemModelTypes.MTOServiceItemDestSIT], + ['internationalOriginSITServiceItemForm', createServiceItemModelTypes.MTOServiceItemInternationalOriginSIT], + ['internationalDestinationSITServiceItemForm', createServiceItemModelTypes.MTOServiceItemInternationalDestSIT], ['shuttleSITServiceItemForm', createServiceItemModelTypes.MTOServiceItemShuttle], ['DomesticCratingForm', createServiceItemModelTypes.MTOServiceItemDomesticCrating], ['InternationalCratingForm', createServiceItemModelTypes.MTOServiceItemInternationalCrating], diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/DestinationSITServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/DestinationSITServiceItemForm.jsx index f2b9832fd00..0eef9278cca 100644 --- a/src/components/PrimeUI/CreateShipmentServiceItemForm/DestinationSITServiceItemForm.jsx +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/DestinationSITServiceItemForm.jsx @@ -25,12 +25,12 @@ const destinationSITValidationSchema = Yup.object().shape({ sitDepartureDate: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), }); -const DestinationSITServiceItemForm = ({ shipment, submission, isDomestic }) => { +const DestinationSITServiceItemForm = ({ shipment, submission }) => { const initialValues = { moveTaskOrderID: shipment.moveTaskOrderID, mtoShipmentID: shipment.id, - modelType: isDomestic ? 'MTOServiceItemDestSIT' : 'MTOServiceItemInternationalDestSIT', - reServiceCode: isDomestic ? 'DDFSIT' : 'IDFSIT', + modelType: 'MTOServiceItemDestSIT', + reServiceCode: 'DDFSIT', reason: '', firstAvailableDeliveryDate1: '', dateOfContact1: '', @@ -111,22 +111,10 @@ const DestinationSITServiceItemForm = ({ shipment, submission, isDomestic }) => The following service items will be created:
- {isDomestic && ( - <> - DDFSIT (Destination 1st day SIT)
- DDASIT (Destination additional days SIT)
- DDDSIT (Destination SIT delivery)
- DDSFSC (Destination SIT fuel surcharge)
- - )} - {!isDomestic && ( - <> - IDFSIT (Destination 1st day SIT)
- IDASIT (Destination additional days SIT)
- IDDSIT (Destination SIT delivery)
- IDSFSC (Destination SIT fuel surcharge)
- - )} + DDFSIT (Destination 1st day SIT)
+ DDASIT (Destination additional days SIT)
+ DDDSIT (Destination SIT delivery)
+ DDSFSC (Destination SIT fuel surcharge)

NOTE: The above service items will use the current delivery address of the shipment as their final delivery address. Ensure the shipment address is accurate before creating these service items. @@ -140,11 +128,6 @@ const DestinationSITServiceItemForm = ({ shipment, submission, isDomestic }) => DestinationSITServiceItemForm.propTypes = { shipment: ShipmentShape.isRequired, submission: PropTypes.func.isRequired, - isDomestic: PropTypes.bool, -}; - -DestinationSITServiceItemForm.defaultProps = { - isDomestic: true, }; export default DestinationSITServiceItemForm; diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/DestinationSITServiceItemForm.test.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/DestinationSITServiceItemForm.test.jsx index 99fbbd18e7a..d87d31e54f2 100644 --- a/src/components/PrimeUI/CreateShipmentServiceItemForm/DestinationSITServiceItemForm.test.jsx +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/DestinationSITServiceItemForm.test.jsx @@ -101,24 +101,6 @@ describe('DestinationSITServiceItemForm component', () => { ); }); - it('renders hint component at bottom of page - international', async () => { - const shipment = approvedMoveTaskOrder.moveTaskOrder.mtoShipments[0]; - - render(); - - const hintInfo = screen.getByTestId('destinationSitInfo'); - expect(hintInfo).toBeInTheDocument(); - - expect(hintInfo).toHaveTextContent('The following service items will be created:'); - expect(hintInfo).toHaveTextContent('IDFSIT (Destination 1st day SIT)'); - expect(hintInfo).toHaveTextContent('IDASIT (Destination additional days SIT)'); - expect(hintInfo).toHaveTextContent('IDDSIT (Destination SIT delivery)'); - expect(hintInfo).toHaveTextContent('IDSFSC (Destination SIT fuel surcharge)'); - expect(hintInfo).toHaveTextContent( - 'NOTE: The above service items will use the current delivery address of the shipment as their final delivery address. Ensure the shipment address is accurate before creating these service items.', - ); - }); - it('renders the Create Service Item button', async () => { const shipment = approvedMoveTaskOrder.moveTaskOrder.mtoShipments[0]; @@ -167,43 +149,4 @@ describe('DestinationSITServiceItemForm component', () => { }, }); }); - - it('submits values when create service item button is clicked for international destination SIT', async () => { - const shipment = approvedMoveTaskOrder.moveTaskOrder.mtoShipments[0]; - const submissionMock = jest.fn(); - - render(); - - await userEvent.type(screen.getByLabelText('Reason'), 'Testing'); - await userEvent.type(screen.getByLabelText('First available delivery date'), '01 Feb 2024'); - await userEvent.type(screen.getByLabelText('First date of attempted contact'), '28 Dec 2023'); - await userEvent.type(screen.getByLabelText('First time of attempted contact'), '1400Z'); - await userEvent.type(screen.getByLabelText('Second available delivery date'), '05 Feb 2024'); - await userEvent.type(screen.getByLabelText('Second date of attempted contact'), '05 Jan 2024'); - await userEvent.type(screen.getByLabelText('Second time of attempted contact'), '1400Z'); - await userEvent.type(screen.getByLabelText('SIT entry date'), '10 Jan 2024'); - await userEvent.type(screen.getByLabelText('SIT departure date'), '24 Jan 2024'); - - // Submit form - await userEvent.click(screen.getByRole('button', { name: 'Create service item' })); - expect(submissionMock).toHaveBeenCalledTimes(1); - expect(submissionMock).toHaveBeenCalledWith({ - body: { - reason: 'Testing', - dateOfContact1: '2023-12-28', - dateOfContact2: '2024-01-05', - firstAvailableDeliveryDate1: '2024-02-01', - firstAvailableDeliveryDate2: '2024-02-05', - modelType: 'MTOServiceItemInternationalDestSIT', - moveTaskOrderID: '9c7b255c-2981-4bf8-839f-61c7458e2b4d', - mtoShipmentID: 'ce01a5b8-9b44-4511-8a8d-edb60f2a4aee', - reServiceCode: 'IDFSIT', - sitDepartureDate: '2024-01-24', - sitDestinationFinalAddress: null, - sitEntryDate: '2024-01-10', - timeMilitary1: '1400Z', - timeMilitary2: '1400Z', - }, - }); - }); }); diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.jsx new file mode 100644 index 00000000000..0a4c4ffeb70 --- /dev/null +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.jsx @@ -0,0 +1,133 @@ +import * as Yup from 'yup'; +import { Formik } from 'formik'; +import { Button } from '@trussworks/react-uswds'; +import React from 'react'; +import PropTypes from 'prop-types'; + +import { Form } from 'components/form/Form'; +import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextField'; +import { formatDateForSwagger } from 'shared/dates'; +import { formatAddressForPrimeAPI } from 'utils/formatters'; +import { DatePickerInput } from 'components/form/fields'; +import { ShipmentShape } from 'types/shipment'; +import TextField from 'components/form/fields/TextField/TextField'; +import Hint from 'components/Hint'; + +const destinationSITValidationSchema = Yup.object().shape({ + reason: Yup.string().required('Required'), + firstAvailableDeliveryDate1: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), + timeMilitary1: Yup.string().matches(/^(\d{4}Z)$/, 'Must be a valid military time (e.g. 1400Z)'), + firstAvailableDeliveryDate2: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), + timeMilitary2: Yup.string().matches(/^(\d{4}Z)$/, 'Must be a valid military time (e.g. 1400Z)'), + sitEntryDate: Yup.date() + .typeError('Enter a complete date in DD MMM YYYY format (day, month, year).') + .required('Required'), + sitDepartureDate: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), +}); + +const InternationalDestinationSITServiceItemForm = ({ shipment, submission }) => { + const initialValues = { + moveTaskOrderID: shipment.moveTaskOrderID, + mtoShipmentID: shipment.id, + modelType: 'MTOServiceItemInternationalDestSIT', + reServiceCode: 'IDFSIT', + reason: '', + firstAvailableDeliveryDate1: '', + dateOfContact1: '', + timeMilitary1: '', + firstAvailableDeliveryDate2: '', + dateOfContact2: '', + timeMilitary2: '', + sitEntryDate: '', + sitDepartureDate: '', + sitDestinationFinalAddress: { streetAddress1: '', streetAddress2: '', city: '', state: '', postalCode: '' }, + }; + + const onSubmit = (values) => { + const { + firstAvailableDeliveryDate1, + firstAvailableDeliveryDate2, + sitEntryDate, + sitDepartureDate, + sitDestinationFinalAddress, + timeMilitary1, + timeMilitary2, + dateOfContact1, + dateOfContact2, + ...serviceItemValues + } = values; + const body = { + firstAvailableDeliveryDate1: formatDateForSwagger(firstAvailableDeliveryDate1), + firstAvailableDeliveryDate2: formatDateForSwagger(firstAvailableDeliveryDate2), + dateOfContact1: formatDateForSwagger(dateOfContact1), + dateOfContact2: formatDateForSwagger(dateOfContact2), + sitEntryDate: formatDateForSwagger(sitEntryDate), + sitDepartureDate: sitDepartureDate ? formatDateForSwagger(sitDepartureDate) : null, + sitDestinationFinalAddress: sitDestinationFinalAddress.streetAddress1 + ? formatAddressForPrimeAPI(sitDestinationFinalAddress) + : null, + timeMilitary1: timeMilitary1 || null, + timeMilitary2: timeMilitary2 || null, + ...serviceItemValues, + }; + submission({ body }); + }; + + return ( + +
+ + + + + + + + + + + + + + + The following service items will be created:
+ IDFSIT (Destination 1st day SIT)
+ IDASIT (Destination additional days SIT)
+ IDDSIT (Destination SIT delivery)
+ IDSFSC (Destination SIT fuel surcharge)
+
+ NOTE: The above service items will use the current delivery address of the shipment as their + final delivery address. Ensure the shipment address is accurate before creating these service items. +
+ + +
+ ); +}; + +InternationalDestinationSITServiceItemForm.propTypes = { + shipment: ShipmentShape.isRequired, + submission: PropTypes.func.isRequired, +}; + +export default InternationalDestinationSITServiceItemForm; diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.test.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.test.jsx new file mode 100644 index 00000000000..f4cce2860c3 --- /dev/null +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalDestinationSITServiceItemForm.test.jsx @@ -0,0 +1,156 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import InternationalDestinationSITServiceItemForm from './InternationalDestinationSITServiceItemForm'; + +const approvedMoveTaskOrder = { + moveTaskOrder: { + id: '9c7b255c-2981-4bf8-839f-61c7458e2b4d', + moveCode: 'LR4T8V', + mtoShipments: [ + { + actualPickupDate: '2020-03-17', + agents: [], + approvedDate: '2021-10-20', + createdAt: '2021-10-21', + customerRemarks: 'Please treat gently', + destinationAddress: { + city: 'Fairfield', + id: 'bfe61147-5fd7-426e-b473-54ccf77bde35', + postalCode: '94535', + state: 'CA', + streetAddress1: '987 Any Avenue', + streetAddress2: 'P.O. Box 9876', + streetAddress3: 'c/o Some Person', + }, + eTag: 'MjAyMS0xMC0xOFQxODoyNDo0MS4zNzc5Nzha', + firstAvailableDeliveryDate: null, + id: 'ce01a5b8-9b44-4511-8a8d-edb60f2a4aee', + moveTaskOrderID: '9c7b255c-2981-4bf8-839f-61c7458e2b4d', + pickupAddress: { + city: 'Beverly Hills', + id: 'cf159eca-162c-4131-84a0-795e684416a6', + postalCode: '90210', + state: 'CA', + streetAddress1: '123 Any Street', + streetAddress2: 'P.O. Box 12345', + streetAddress3: 'c/o Some Person', + }, + primeActualWeight: 2000, + primeEstimatedWeight: 1400, + primeEstimatedWeightRecordedDate: null, + requestedPickupDate: '2020-03-15', + requiredDeliveryDate: null, + scheduledPickupDate: '2020-03-16', + secondaryDeliveryAddress: { + city: null, + postalCode: null, + state: null, + streetAddress1: null, + }, + shipmentType: 'HHG', + status: 'APPROVED', + updatedAt: '2021-10-22', + mtoServiceItems: null, + reweigh: { + id: '1234', + weight: 9000, + requestedAt: '2021-10-23', + }, + }, + ], + }, +}; + +describe('InternationalDestinationSITServiceItemForm component', () => { + it.each([ + ['Reason', 'reason'], + ['First available delivery date', 'firstAvailableDeliveryDate1'], + ['First date of attempted contact', 'dateOfContact1'], + ['First time of attempted contact', 'timeMilitary1'], + ['Second available delivery date', 'firstAvailableDeliveryDate2'], + ['Second date of attempted contact', 'dateOfContact2'], + ['Second time of attempted contact', 'timeMilitary2'], + ['SIT entry date', 'sitEntryDate'], + ['SIT departure date', 'sitDepartureDate'], + ])('renders field %s in form', (labelName) => { + const shipment = approvedMoveTaskOrder.moveTaskOrder.mtoShipments[0]; + + render(); + + const field = screen.getByText(labelName); + expect(field).toBeInTheDocument(); + }); + + it('renders hint component at bottom of page - international', async () => { + const shipment = approvedMoveTaskOrder.moveTaskOrder.mtoShipments[0]; + + render( + , + ); + + const hintInfo = screen.getByTestId('destinationSitInfo'); + expect(hintInfo).toBeInTheDocument(); + + expect(hintInfo).toHaveTextContent('The following service items will be created:'); + expect(hintInfo).toHaveTextContent('IDFSIT (Destination 1st day SIT)'); + expect(hintInfo).toHaveTextContent('IDASIT (Destination additional days SIT)'); + expect(hintInfo).toHaveTextContent('IDDSIT (Destination SIT delivery)'); + expect(hintInfo).toHaveTextContent('IDSFSC (Destination SIT fuel surcharge)'); + expect(hintInfo).toHaveTextContent( + 'NOTE: The above service items will use the current delivery address of the shipment as their final delivery address. Ensure the shipment address is accurate before creating these service items.', + ); + }); + + it('renders the Create Service Item button', async () => { + const shipment = approvedMoveTaskOrder.moveTaskOrder.mtoShipments[0]; + + render(); + + // Check if the button renders + const createBtn = screen.getByRole('button', { name: 'Create service item' }); + expect(createBtn).toBeInTheDocument(); + }); + + it('submits values when create service item button is clicked for international destination SIT', async () => { + const shipment = approvedMoveTaskOrder.moveTaskOrder.mtoShipments[0]; + const submissionMock = jest.fn(); + + render( + , + ); + + await userEvent.type(screen.getByLabelText('Reason'), 'Testing'); + await userEvent.type(screen.getByLabelText('First available delivery date'), '01 Feb 2024'); + await userEvent.type(screen.getByLabelText('First date of attempted contact'), '28 Dec 2023'); + await userEvent.type(screen.getByLabelText('First time of attempted contact'), '1400Z'); + await userEvent.type(screen.getByLabelText('Second available delivery date'), '05 Feb 2024'); + await userEvent.type(screen.getByLabelText('Second date of attempted contact'), '05 Jan 2024'); + await userEvent.type(screen.getByLabelText('Second time of attempted contact'), '1400Z'); + await userEvent.type(screen.getByLabelText('SIT entry date'), '10 Jan 2024'); + await userEvent.type(screen.getByLabelText('SIT departure date'), '24 Jan 2024'); + + // Submit form + await userEvent.click(screen.getByRole('button', { name: 'Create service item' })); + expect(submissionMock).toHaveBeenCalledTimes(1); + expect(submissionMock).toHaveBeenCalledWith({ + body: { + reason: 'Testing', + dateOfContact1: '2023-12-28', + dateOfContact2: '2024-01-05', + firstAvailableDeliveryDate1: '2024-02-01', + firstAvailableDeliveryDate2: '2024-02-05', + modelType: 'MTOServiceItemInternationalDestSIT', + moveTaskOrderID: '9c7b255c-2981-4bf8-839f-61c7458e2b4d', + mtoShipmentID: 'ce01a5b8-9b44-4511-8a8d-edb60f2a4aee', + reServiceCode: 'IDFSIT', + sitDepartureDate: '2024-01-24', + sitDestinationFinalAddress: null, + sitEntryDate: '2024-01-10', + timeMilitary1: '1400Z', + timeMilitary2: '1400Z', + }, + }); + }); +}); diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.jsx new file mode 100644 index 00000000000..76db6a191d6 --- /dev/null +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.jsx @@ -0,0 +1,110 @@ +import * as Yup from 'yup'; +import { Formik } from 'formik'; +import { Button } from '@trussworks/react-uswds'; +import React from 'react'; +import { useNavigate, useParams, generatePath } from 'react-router-dom'; +import PropTypes from 'prop-types'; + +import { requiredAddressSchema, ZIP_CODE_REGEX } from 'utils/validation'; +import { formatDateForSwagger } from 'shared/dates'; +import { formatAddressForPrimeAPI } from 'utils/formatters'; +import { Form } from 'components/form/Form'; +import TextField from 'components/form/fields/TextField/TextField'; +import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextField'; +import { DatePickerInput } from 'components/form/fields'; +import { AddressFields } from 'components/form/AddressFields/AddressFields'; +import { ShipmentShape } from 'types/shipment'; +import { primeSimulatorRoutes } from 'constants/routes'; + +const originSITValidationSchema = Yup.object().shape({ + reason: Yup.string().required('Required'), + sitPostalCode: Yup.string().matches(ZIP_CODE_REGEX, 'Must be valid zip code').required('Required'), + sitEntryDate: Yup.date() + .typeError('Enter a complete date in DD MMM YYYY format (day, month, year).') + .required('Required'), + sitDepartureDate: Yup.date().typeError('Enter a complete date in DD MMM YYYY format (day, month, year).'), + sitHHGActualOrigin: requiredAddressSchema, +}); + +const InternationalOriginSITServiceItemForm = ({ shipment, submission }) => { + const initialValues = { + moveTaskOrderID: shipment.moveTaskOrderID, + mtoShipmentID: shipment.id, + modelType: 'MTOServiceItemInternationalOriginSIT', + reServiceCode: 'IOFSIT', + reason: '', + sitPostalCode: '', + sitEntryDate: '', + sitDepartureDate: '', // The Prime API is currently ignoring origin SIT departure date on creation + sitHHGActualOrigin: { + streetAddress1: '', + streetAddress2: '', + streetAddress3: '', + city: '', + state: '', + postalCode: '', + county: '', + }, + }; + + const onSubmit = (values) => { + const { sitEntryDate, sitDepartureDate, sitHHGActualOrigin, ...serviceItemValues } = values; + const body = { + sitEntryDate: formatDateForSwagger(sitEntryDate), + sitDepartureDate: sitDepartureDate ? formatDateForSwagger(sitDepartureDate) : null, + sitHHGActualOrigin: sitHHGActualOrigin.streetAddress1 ? formatAddressForPrimeAPI(sitHHGActualOrigin) : null, + ...serviceItemValues, + }; + submission({ body }); + }; + + const { moveCodeOrID } = useParams(); + const navigate = useNavigate(); + const handleCancel = () => { + navigate(generatePath(primeSimulatorRoutes.VIEW_MOVE_PATH, { moveCodeOrID })); + }; + + return ( + + {({ isValid, isSubmitting, handleSubmit, ...formikProps }) => { + return ( +
+ + + + + + + + + + + + + ); + }} +
+ ); +}; + +InternationalOriginSITServiceItemForm.propTypes = { + shipment: ShipmentShape.isRequired, + submission: PropTypes.func.isRequired, +}; + +export default InternationalOriginSITServiceItemForm; diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.test.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.test.jsx new file mode 100644 index 00000000000..f7445e10751 --- /dev/null +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/InternationalOriginSITServiceItemForm.test.jsx @@ -0,0 +1,96 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; + +import InternationalOriginSITServiceItemForm from './InternationalOriginSITServiceItemForm'; + +import { MockProviders } from 'testUtils'; + +const approvedMoveTaskOrder = { + moveTaskOrder: { + id: '9c7b255c-2981-4bf8-839f-61c7458e2b4d', + moveCode: 'LR4T8V', + mtoShipments: [ + { + actualPickupDate: '2020-03-17', + agents: [], + approvedDate: '2021-10-20', + createdAt: '2021-10-21', + customerRemarks: 'Please treat gently', + destinationAddress: { + city: 'Fairfield', + id: 'bfe61147-5fd7-426e-b473-54ccf77bde35', + postalCode: '94535', + state: 'CA', + streetAddress1: '987 Any Avenue', + streetAddress2: 'P.O. Box 9876', + streetAddress3: 'c/o Some Person', + }, + eTag: 'MjAyMS0xMC0xOFQxODoyNDo0MS4zNzc5Nzha', + firstAvailableDeliveryDate: null, + id: 'ce01a5b8-9b44-4511-8a8d-edb60f2a4aee', + moveTaskOrderID: '9c7b255c-2981-4bf8-839f-61c7458e2b4d', + pickupAddress: { + city: 'Beverly Hills', + id: 'cf159eca-162c-4131-84a0-795e684416a6', + postalCode: '90210', + state: 'CA', + streetAddress1: '123 Any Street', + streetAddress2: 'P.O. Box 12345', + streetAddress3: 'c/o Some Person', + }, + primeActualWeight: 2000, + primeEstimatedWeight: 1400, + primeEstimatedWeightRecordedDate: null, + requestedPickupDate: '2020-03-15', + requiredDeliveryDate: null, + scheduledPickupDate: '2020-03-16', + secondaryDeliveryAddress: { + city: null, + postalCode: null, + state: null, + streetAddress1: null, + }, + shipmentType: 'HHG', + status: 'APPROVED', + updatedAt: '2021-10-22', + mtoServiceItems: null, + reweigh: { + id: '1234', + weight: 9000, + requestedAt: '2021-10-23', + }, + }, + ], + }, +}; + +const renderWithProviders = (component) => { + render({component}); +}; + +describe('InternationalOriginSITServiceItemForm component', () => { + it.each([ + ['Reason', 'reason'], + ['SIT postal code', 'sitPostalCode'], + ['SIT entry Date', 'sitEntryDate'], + ['SIT departure Date', 'sitDepartureDate'], + ['SIT HHG actual origin', 'sitHHGActualOrigin'], + ])('renders field %s in form', (labelName) => { + const shipment = approvedMoveTaskOrder.moveTaskOrder.mtoShipments[0]; + + renderWithProviders(); + + const field = screen.getByText(labelName); + expect(field).toBeInTheDocument(); + }); + + it('renders the Create Service Item button', async () => { + const shipment = approvedMoveTaskOrder.moveTaskOrder.mtoShipments[0]; + + renderWithProviders(); + + // Check if the button renders + const createBtn = screen.getByRole('button', { name: 'Create service item' }); + expect(createBtn).toBeInTheDocument(); + }); +}); diff --git a/src/components/PrimeUI/CreateShipmentServiceItemForm/OriginSITServiceItemForm.jsx b/src/components/PrimeUI/CreateShipmentServiceItemForm/OriginSITServiceItemForm.jsx index f2e2f9bd55b..dc86b6ad5e1 100644 --- a/src/components/PrimeUI/CreateShipmentServiceItemForm/OriginSITServiceItemForm.jsx +++ b/src/components/PrimeUI/CreateShipmentServiceItemForm/OriginSITServiceItemForm.jsx @@ -26,12 +26,12 @@ const originSITValidationSchema = Yup.object().shape({ sitHHGActualOrigin: requiredAddressSchema, }); -const OriginSITServiceItemForm = ({ shipment, submission, isDomestic }) => { +const OriginSITServiceItemForm = ({ shipment, submission }) => { const initialValues = { moveTaskOrderID: shipment.moveTaskOrderID, mtoShipmentID: shipment.id, - modelType: isDomestic ? 'MTOServiceItemOriginSIT' : 'MTOServiceItemInternationalOriginSIT', - reServiceCode: isDomestic ? 'DOFSIT' : 'IOFSIT', + modelType: 'MTOServiceItemOriginSIT', + reServiceCode: 'DOFSIT', reason: '', sitPostalCode: '', sitEntryDate: '', @@ -105,11 +105,6 @@ const OriginSITServiceItemForm = ({ shipment, submission, isDomestic }) => { OriginSITServiceItemForm.propTypes = { shipment: ShipmentShape.isRequired, submission: PropTypes.func.isRequired, - isDomestic: PropTypes.bool, -}; - -OriginSITServiceItemForm.defaultProps = { - isDomestic: true, }; export default OriginSITServiceItemForm; From 793beb95071191019af47523b657532cc7395682 Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Wed, 29 Jan 2025 16:31:55 +0000 Subject: [PATCH 06/26] deleted function not used to increase code coverage and small changes per PR comments --- .../primeapi/payloads/payload_to_model.go | 6 +- .../primeapiv2/payloads/payload_to_model.go | 4 +- pkg/models/mto_shipments.go | 27 ------ .../ServiceItemDetails/ServiceItemDetails.jsx | 89 ++++++++++--------- src/constants/serviceItems.js | 1 + 5 files changed, 51 insertions(+), 76 deletions(-) diff --git a/pkg/handlers/primeapi/payloads/payload_to_model.go b/pkg/handlers/primeapi/payloads/payload_to_model.go index 65154580f1f..7de1066f70a 100644 --- a/pkg/handlers/primeapi/payloads/payload_to_model.go +++ b/pkg/handlers/primeapi/payloads/payload_to_model.go @@ -583,7 +583,7 @@ func MTOServiceItemModel(mtoServiceItem primemessages.MTOServiceItem) (*models.M model.Reason = destsit.Reason sitEntryDate := handlers.FmtDatePtrToPopPtr(destsit.SitEntryDate) - // Check for required fields on a DDFSIT + // Check for required fields on a IDFSIT if model.ReService.Code == models.ReServiceCodeIDFSIT { verrs := validateIDFSITForCreate(*destsit) reasonVerrs := validateReasonInternationalDestSIT(*destsit) @@ -954,7 +954,7 @@ func validateDDFSITForCreate(m primemessages.MTOServiceItemDestSIT) *validate.Er return verrs } -// validateIDFSITForCreate validates DDFSIT service item has all required fields +// validateIDFSITForCreate validates IDFSIT service item has all required fields func validateIDFSITForCreate(m primemessages.MTOServiceItemInternationalDestSIT) *validate.Errors { verrs := validate.NewErrors() @@ -971,7 +971,7 @@ func validateIDFSITForCreate(m primemessages.MTOServiceItemInternationalDestSIT) verrs.Add("firstAvailableDeliveryDate2", "firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 must be provided together in body.") } if m.DateOfContact2 == nil && m.TimeMilitary2 != nil && m.FirstAvailableDeliveryDate2 != nil { - verrs.Add("DateOfContact1", "dateOfContact2, firstAvailableDeliveryDate2, and timeMilitary2 must be provided together in body.") + verrs.Add("DateOfContact2", "dateOfContact2, firstAvailableDeliveryDate2, and timeMilitary2 must be provided together in body.") } if m.TimeMilitary2 == nil && m.DateOfContact2 != nil && m.FirstAvailableDeliveryDate2 != nil { verrs.Add("timeMilitary2", "timeMilitary2, firstAvailableDeliveryDate2, and dateOfContact2 must be provided together in body.") diff --git a/pkg/handlers/primeapiv2/payloads/payload_to_model.go b/pkg/handlers/primeapiv2/payloads/payload_to_model.go index 17c662c958c..862a774ecc2 100644 --- a/pkg/handlers/primeapiv2/payloads/payload_to_model.go +++ b/pkg/handlers/primeapiv2/payloads/payload_to_model.go @@ -1019,7 +1019,7 @@ func validateDDFSITForCreate(m primev2messages.MTOServiceItemDestSIT) *validate. return verrs } -// validateIDFSITForCreate validates DDFSIT service item has all required fields +// validateIDFSITForCreate validates IDFSIT service item has all required fields func validateIDFSITForCreate(m primev2messages.MTOServiceItemInternationalDestSIT) *validate.Errors { verrs := validate.NewErrors() @@ -1036,7 +1036,7 @@ func validateIDFSITForCreate(m primev2messages.MTOServiceItemInternationalDestSI verrs.Add("firstAvailableDeliveryDate2", "firstAvailableDeliveryDate2, dateOfContact2, and timeMilitary2 must be provided together in body.") } if m.DateOfContact2 == nil && m.TimeMilitary2 != nil && m.FirstAvailableDeliveryDate2 != nil { - verrs.Add("DateOfContact1", "dateOfContact2, firstAvailableDeliveryDate2, and timeMilitary2 must be provided together in body.") + verrs.Add("DateOfContact2", "dateOfContact2, firstAvailableDeliveryDate2, and timeMilitary2 must be provided together in body.") } if m.TimeMilitary2 == nil && m.DateOfContact2 != nil && m.FirstAvailableDeliveryDate2 != nil { verrs.Add("timeMilitary2", "timeMilitary2, firstAvailableDeliveryDate2, and dateOfContact2 must be provided together in body.") diff --git a/pkg/models/mto_shipments.go b/pkg/models/mto_shipments.go index 53899c73eed..7b8d6b35db1 100644 --- a/pkg/models/mto_shipments.go +++ b/pkg/models/mto_shipments.go @@ -296,33 +296,6 @@ func GetCustomerFromShipment(db *pop.Connection, shipmentID uuid.UUID) (*Service return &serviceMember, nil } -func (m *MTOShipment) UpdateOrdersDestinationGBLOC(db *pop.Connection) error { - // Since this requires looking up the order in the DB, the order must have an ID. This means, the order has to have been created first. - if uuid.UUID.IsNil(m.ID) { - return fmt.Errorf("error updating orders destination GBLOC for shipment due to no shipment ID provided") - } - - var err error - var order Order - - err = db.Load(&m, "MoveTaskOrder.OrdersID") - if err != nil { - return fmt.Errorf("error loading orders for shipment ID: %s with error %w", m.ID, err) - } - - order, err = FetchOrder(db, m.MoveTaskOrder.OrdersID) - if err != nil { - return fmt.Errorf("error fetching order for shipment ID: %s with error %w", m.ID, err) - } - - err = order.UpdateDestinationGBLOC(db) - if err != nil { - return fmt.Errorf("error fetching GBLOC for postal code with error %w", err) - } - - return nil -} - // Helper function to check that an MTO Shipment contains a PPM Shipment func (m MTOShipment) ContainsAPPMShipment() bool { return m.PPMShipment != nil diff --git a/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx b/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx index 2f6a90099c3..c406dff1c7b 100644 --- a/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx +++ b/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx @@ -13,6 +13,7 @@ import { SitStatusShape } from 'types/sitStatusShape'; import { formatDateWithUTC } from 'shared/dates'; import { formatCityStateAndPostalCode } from 'utils/shipmentDisplay'; import { formatWeight, convertFromThousandthInchToInch, formatCents, toDollarString } from 'utils/formatters'; +import { SERVICE_ITEM_CODES } from 'constants/serviceItems'; function generateDetailText(details, id, className) { const detailList = Object.keys(details).map((detail) => ( @@ -51,7 +52,7 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai return (
- {code === 'DDFSIT' || code === 'IDFSIT' + {code === SERVICE_ITEM_CODES.DDFSIT || code === SERVICE_ITEM_CODES.IDFSIT ? generateDetailText({ 'Original Delivery Address': originalDeliveryAddress ? formatCityStateAndPostalCode(originalDeliveryAddress) @@ -59,7 +60,7 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai 'SIT entry date': details.sitEntryDate ? formatDateWithUTC(details.sitEntryDate, 'DD MMM YYYY') : '-', }) : null} - {code === 'DDASIT' && ( + {code === SERVICE_ITEM_CODES.DDASIT && ( <> {generateDetailText( { @@ -88,7 +89,7 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai ) : null} )} - {code === 'IDASIT' && ( + {code === SERVICE_ITEM_CODES.IDASIT && ( <> {generateDetailText( { @@ -117,7 +118,7 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai ) : null} )} - {code === 'DDSFSC' || code === 'IDSFSC' + {code === SERVICE_ITEM_CODES.DDSFSC || code === SERVICE_ITEM_CODES.IDSFSC ? generateDetailText( { 'Original Delivery Address': originalDeliveryAddress @@ -132,7 +133,7 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai id, ) : null} - {code === 'DDDSIT' && ( + {code === SERVICE_ITEM_CODES.DDDSIT && ( <> {generateDetailText( { @@ -170,7 +171,7 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai ) : null} )} - {code === 'IDDSIT' && ( + {code === SERVICE_ITEM_CODES.IDDSIT && ( <> {generateDetailText( { @@ -208,7 +209,7 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai ) : null} )} - {(code === 'DDFSIT' || code === 'IDFSIT') && ( + {(code === SERVICE_ITEM_CODES.DDFSIT || code === SERVICE_ITEM_CODES.IDFSIT) && ( <> {!isEmpty(sortedCustomerContacts) ? sortedCustomerContacts.map((contact, index) => ( @@ -256,8 +257,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s let detailSection; switch (code) { - case 'DOFSIT': - case 'IOFSIT': { + case SERVICE_ITEM_CODES.DOFSIT: + case SERVICE_ITEM_CODES.IOFSIT: { detailSection = (
@@ -290,8 +291,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DOASIT': - case 'IOASIT': { + case SERVICE_ITEM_CODES.DOASIT: + case SERVICE_ITEM_CODES.IOASIT: { const numberOfDaysApprovedForIOASIT = shipment.sitDaysAllowance ? shipment.sitDaysAllowance - 1 : 0; const sitEndDate = sitStatus && @@ -342,8 +343,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DOPSIT': - case 'IOPSIT': { + case SERVICE_ITEM_CODES.DOPSIT: + case SERVICE_ITEM_CODES.IOPSIT: { detailSection = (
@@ -378,8 +379,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DOSFSC': - case 'IOSFSC': { + case SERVICE_ITEM_CODES.DOSFSC: + case SERVICE_ITEM_CODES.IOSFSC: { detailSection = (
@@ -414,10 +415,10 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DDFSIT': - case 'DDASIT': - case 'IDFSIT': - case 'IDASIT': { + case SERVICE_ITEM_CODES.DDFSIT: + case SERVICE_ITEM_CODES.DDASIT: + case SERVICE_ITEM_CODES.IDFSIT: + case SERVICE_ITEM_CODES.IDASIT: { detailSection = generateDestinationSITDetailSection( id, serviceRequestDocUploads, @@ -428,8 +429,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DDDSIT': - case 'IDDSIT': { + case SERVICE_ITEM_CODES.DDDSIT: + case SERVICE_ITEM_CODES.IDDSIT: { detailSection = generateDestinationSITDetailSection( id, serviceRequestDocUploads, @@ -440,8 +441,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DDSFSC': - case 'IDSFSC': { + case SERVICE_ITEM_CODES.DDSFSC: + case SERVICE_ITEM_CODES.IDSFSC: { detailSection = generateDestinationSITDetailSection( id, serviceRequestDocUploads, @@ -452,8 +453,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DCRT': - case 'DCRTSA': { + case SERVICE_ITEM_CODES.DCRT: + case SERVICE_ITEM_CODES.DCRTSA: { const { description, itemDimensions, crateDimensions } = details; const itemDimensionFormat = `${convertFromThousandthInchToInch( itemDimensions?.length, @@ -491,7 +492,7 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DUCRT': { + case SERVICE_ITEM_CODES.DUCRT: { const { description, itemDimensions, crateDimensions } = details; const itemDimensionFormat = `${convertFromThousandthInchToInch( itemDimensions?.length, @@ -528,8 +529,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DOSHUT': - case 'DDSHUT': { + case SERVICE_ITEM_CODES.DOSHUT: + case SERVICE_ITEM_CODES.DDSHUT: { const estimatedWeight = details.estimatedWeight != null ? formatWeight(details.estimatedWeight) : `— lbs`; detailSection = (
@@ -557,18 +558,18 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'DLH': - case 'DSH': - case 'FSC': - case 'DOP': - case 'DDP': - case 'DPK': - case 'DUPK': - case 'ISLH': - case 'IHPK': - case 'IHUPK': - case 'POEFSC': - case 'PODFSC': { + case SERVICE_ITEM_CODES.DLH: + case SERVICE_ITEM_CODES.DSH: + case SERVICE_ITEM_CODES.FSC: + case SERVICE_ITEM_CODES.DOP: + case SERVICE_ITEM_CODES.DDP: + case SERVICE_ITEM_CODES.DPK: + case SERVICE_ITEM_CODES.DUPK: + case SERVICE_ITEM_CODES.ISLH: + case SERVICE_ITEM_CODES.IHPK: + case SERVICE_ITEM_CODES.IHUPK: + case SERVICE_ITEM_CODES.POEFSC: + case SERVICE_ITEM_CODES.PODFSC: { detailSection = (
@@ -580,8 +581,8 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'MS': - case 'CS': { + case SERVICE_ITEM_CODES.MS: + case SERVICE_ITEM_CODES.CS: { const { estimatedPrice } = details; detailSection = (
@@ -590,7 +591,7 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'ICRT': { + case SERVICE_ITEM_CODES.ICRT: { const { description, itemDimensions, crateDimensions, market, externalCrate } = details; const itemDimensionFormat = `${convertFromThousandthInchToInch( itemDimensions?.length, @@ -630,7 +631,7 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s ); break; } - case 'IUCRT': { + case SERVICE_ITEM_CODES.IUCRT: { const { description, itemDimensions, crateDimensions, market } = details; const itemDimensionFormat = `${convertFromThousandthInchToInch( itemDimensions?.length, diff --git a/src/constants/serviceItems.js b/src/constants/serviceItems.js index 89fccf775bd..9aafadad1e4 100644 --- a/src/constants/serviceItems.js +++ b/src/constants/serviceItems.js @@ -146,6 +146,7 @@ const SERVICE_ITEM_CODES = { FSC: 'FSC', DDSHUT: 'DDSHUT', IDSHUT: 'IDSHUT', + DCRTSA: 'DCRTSA', DCRT: 'DCRT', DUCRT: 'DUCRT', ICRT: 'ICRT', From 7fc7b46b73f0bb722bcf28df37578080daef777c Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Fri, 31 Jan 2025 15:29:11 +0000 Subject: [PATCH 07/26] Fix comments --- pkg/handlers/primeapiv2/payloads/payload_to_model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/handlers/primeapiv2/payloads/payload_to_model.go b/pkg/handlers/primeapiv2/payloads/payload_to_model.go index 862a774ecc2..1f99aa3a009 100644 --- a/pkg/handlers/primeapiv2/payloads/payload_to_model.go +++ b/pkg/handlers/primeapiv2/payloads/payload_to_model.go @@ -1079,7 +1079,7 @@ func validateReasonDestSIT(m primev2messages.MTOServiceItemDestSIT) *validate.Er return verrs } -// validateReasonDestSIT validates that International Destination SIT service items have required Reason field +// validateReasonInternationalDestSIT validates that International Destination SIT service items have required Reason field func validateReasonInternationalDestSIT(m primev2messages.MTOServiceItemInternationalDestSIT) *validate.Errors { verrs := validate.NewErrors() From fd6e17877d5d702c08982af7e02724bcd506f898 Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Fri, 31 Jan 2025 16:27:15 +0000 Subject: [PATCH 08/26] fix comments and code cleanup per PR comments --- .../primeapiv3/payloads/payload_to_model.go | 2 +- .../mto_service_item_validators_test.go | 18 ++++----- .../sit_entry_date_updater_test.go | 18 ++++----- .../ServiceItemsTable/ServiceItemsTable.jsx | 38 ++++++++++++++++--- 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/pkg/handlers/primeapiv3/payloads/payload_to_model.go b/pkg/handlers/primeapiv3/payloads/payload_to_model.go index c31f96166f6..470d855095a 100644 --- a/pkg/handlers/primeapiv3/payloads/payload_to_model.go +++ b/pkg/handlers/primeapiv3/payloads/payload_to_model.go @@ -1244,7 +1244,7 @@ func validateReasonDestSIT(m primev3messages.MTOServiceItemDestSIT) *validate.Er return verrs } -// validateReasonDestSIT validates that International Destination SIT service items have required Reason field +// validateReasonInternationalDestSIT validates that International Destination SIT service items have required Reason field func validateReasonInternationalDestSIT(m primev3messages.MTOServiceItemInternationalDestSIT) *validate.Errors { verrs := validate.NewErrors() diff --git a/pkg/services/mto_service_item/mto_service_item_validators_test.go b/pkg/services/mto_service_item/mto_service_item_validators_test.go index e8932bee2ee..ca918f93650 100644 --- a/pkg/services/mto_service_item/mto_service_item_validators_test.go +++ b/pkg/services/mto_service_item/mto_service_item_validators_test.go @@ -296,23 +296,23 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemData() { // Test successful check for SIT departure service item - IDDSIT suite.Run("checkSITDeparture w/ IDDSIT - success", func() { // Under test: checkSITDeparture checks that the service item is a - // DDDSIT or DOPSIT if the user is trying to update the + // IDDSIT or IOPSIT if the user is trying to update the // SITDepartureDate - // Set up: Create an old and new DDDSIT, with a new date and try to update. - // Expected outcome: Success if both are DDDSIT - oldDDDSIT := factory.BuildMTOServiceItem(nil, []factory.Customization{ + // Set up: Create an old and new IDDSIT, with a new date and try to update. + // Expected outcome: Success if both are IDDSIT + oldIDDSIT := factory.BuildMTOServiceItem(nil, []factory.Customization{ { Model: models.ReService{ Code: models.ReServiceCodeIDDSIT, }, }, }, nil) - newDDDSIT := oldDDDSIT - newDDDSIT.SITDepartureDate = &now + newIDDSIT := oldIDDSIT + newIDDSIT.SITDepartureDate = &now serviceItemData := updateMTOServiceItemData{ - updatedServiceItem: newDDDSIT, - oldServiceItem: oldDDDSIT, + updatedServiceItem: newIDDSIT, + oldServiceItem: oldIDDSIT, verrs: validate.NewErrors(), } err := serviceItemData.checkSITDeparture(suite.AppContextForTest()) @@ -321,7 +321,7 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemData() { suite.NoVerrs(serviceItemData.verrs) }) - // Test successful check for SIT departure service item - DDDSIT + // Test successful check for SIT departure service item - IDDSIT suite.Run("checkSITDeparture w/ IDDSIT - success", func() { // Under test: checkSITDeparture checks that the service item is a // IDDSIT or IOPSIT if the user is trying to update the diff --git a/pkg/services/sit_entry_date_update/sit_entry_date_updater_test.go b/pkg/services/sit_entry_date_update/sit_entry_date_updater_test.go index 4f11deab45d..e9c0bb65de6 100644 --- a/pkg/services/sit_entry_date_update/sit_entry_date_updater_test.go +++ b/pkg/services/sit_entry_date_update/sit_entry_date_updater_test.go @@ -105,10 +105,10 @@ func (suite *UpdateSitEntryDateServiceSuite) TestUpdateSitEntryDate() { }) suite.Run("Not Found Error - international", func() { - ddfServiceItem, _ := setupInternationalModels() + idfServiceItem, _ := setupInternationalModels() notFoundServiceItem := models.SITEntryDateUpdate{ - ID: ddfServiceItem.ID, - SITEntryDate: ddfServiceItem.SITEntryDate, + ID: idfServiceItem.ID, + SITEntryDate: idfServiceItem.SITEntryDate, } notFoundUUID, err := uuid.NewV4() suite.NoError(err) @@ -144,24 +144,24 @@ func (suite *UpdateSitEntryDateServiceSuite) TestUpdateSitEntryDate() { }) suite.Run("Successful update of service items - international", func() { - ddfServiceItem, ddaServiceItem := setupInternationalModels() + idfServiceItem, idaServiceItem := setupInternationalModels() updatedServiceItem := models.SITEntryDateUpdate{ - ID: ddfServiceItem.ID, - SITEntryDate: ddfServiceItem.SITEntryDate, + ID: idfServiceItem.ID, + SITEntryDate: idfServiceItem.SITEntryDate, } newSitEntryDate := time.Date(2020, time.December, 02, 0, 0, 0, 0, time.UTC) newSitEntryDateNextDay := newSitEntryDate.Add(24 * time.Hour) updatedServiceItem.SITEntryDate = &newSitEntryDate - ddaServiceItem.SITEntryDate = &newSitEntryDateNextDay + idaServiceItem.SITEntryDate = &newSitEntryDateNextDay changedServiceItem, err := updater.UpdateSitEntryDate(suite.AppContextForTest(), &updatedServiceItem) suite.NoError(err) suite.NotNil(updatedServiceItem) - suite.Equal(ddfServiceItem.ID, updatedServiceItem.ID) + suite.Equal(idfServiceItem.ID, updatedServiceItem.ID) suite.Equal(updatedServiceItem.SITEntryDate.Local(), changedServiceItem.SITEntryDate.Local()) - suite.Equal(ddaServiceItem.SITEntryDate.Local(), newSitEntryDateNextDay.Local()) + suite.Equal(idaServiceItem.SITEntryDate.Local(), newSitEntryDateNextDay.Local()) }) } diff --git a/src/components/Office/ServiceItemsTable/ServiceItemsTable.jsx b/src/components/Office/ServiceItemsTable/ServiceItemsTable.jsx index 214e7f112a2..5495ad0fc48 100644 --- a/src/components/Office/ServiceItemsTable/ServiceItemsTable.jsx +++ b/src/components/Office/ServiceItemsTable/ServiceItemsTable.jsx @@ -10,6 +10,7 @@ import { ServiceItemDetailsShape } from '../../../types/serviceItems'; import styles from './ServiceItemsTable.module.scss'; import { SERVICE_ITEM_STATUS } from 'shared/constants'; +import { SERVICE_ITEM_CODES } from 'constants/serviceItems'; import { ALLOWED_RESUBMISSION_SI_CODES, ALLOWED_SIT_UPDATE_SI_CODES } from 'constants/sitUpdates'; import { formatDateFromIso } from 'utils/formatters'; import ServiceItemDetails from 'components/Office/ServiceItemDetails/ServiceItemDetails'; @@ -30,19 +31,37 @@ import { nullSafeStringCompare } from 'utils/string'; // destination SIT function sortServiceItems(items) { // Prioritize service items with codes 'DSH' (shorthaul) and 'DLH' (linehaul) to be at the top of the list - const haulTypeServiceItemCodes = ['DSH', 'DLH']; + const haulTypeServiceItemCodes = [SERVICE_ITEM_CODES.DSH, SERVICE_ITEM_CODES.DLH]; const haulTypeServiceItems = items.filter((item) => haulTypeServiceItemCodes.includes(item.code)); const sortedHaulTypeServiceItems = haulTypeServiceItems.sort( (a, b) => haulTypeServiceItemCodes.indexOf(a.code) - haulTypeServiceItemCodes.indexOf(b.code), ); // Filter and sort destination SIT. Code index is also the sort order - const destinationServiceItemCodes = ['DDFSIT', 'DDASIT', 'DDDSIT', 'DDSFSC', 'IDFSIT', 'IDASIT', 'IDDSIT', 'IDSFSC']; + const destinationServiceItemCodes = [ + SERVICE_ITEM_CODES.DDFSIT, + SERVICE_ITEM_CODES.DDASIT, + SERVICE_ITEM_CODES.DDDSIT, + SERVICE_ITEM_CODES.DDSFSC, + SERVICE_ITEM_CODES.IDFSIT, + SERVICE_ITEM_CODES.IDASIT, + SERVICE_ITEM_CODES.IDDSIT, + SERVICE_ITEM_CODES.IDSFSC, + ]; const destinationServiceItems = items.filter((item) => destinationServiceItemCodes.includes(item.code)); const sortedDestinationServiceItems = destinationServiceItems.sort( (a, b) => destinationServiceItemCodes.indexOf(a.code) - destinationServiceItemCodes.indexOf(b.code), ); // Filter origin SIT. Code index is also the sort order - const originServiceItemCodes = ['DOFSIT', 'DOASIT', 'DOPSIT', 'DOSFSC', 'IOFSIT', 'IOASIT', 'IOPSIT', 'IOSFSC']; + const originServiceItemCodes = [ + SERVICE_ITEM_CODES.DOFSIT, + SERVICE_ITEM_CODES.DOASIT, + SERVICE_ITEM_CODES.DOPSIT, + SERVICE_ITEM_CODES.DOSFSC, + SERVICE_ITEM_CODES.IOFSIT, + SERVICE_ITEM_CODES.IOASIT, + SERVICE_ITEM_CODES.IOPSIT, + SERVICE_ITEM_CODES.IOSFSC, + ]; const originServiceItems = items.filter((item) => originServiceItemCodes.includes(item.code)); const sortedOriginServiceItems = originServiceItems.sort( (a, b) => originServiceItemCodes.indexOf(a.code) - originServiceItemCodes.indexOf(b.code), @@ -202,7 +221,7 @@ const ServiceItemsTable = ({ // we don't want to display the "Accept" button for a DLH or DSH service item that was rejected by a shorthaul to linehaul change or vice versa let rejectedDSHorDLHServiceItem = false; if ( - (serviceItem.code === 'DLH' || serviceItem.code === 'DSH') && + (serviceItem.code === SERVICE_ITEM_CODES.DLH || serviceItem.code === SERVICE_ITEM_CODES.DSH) && serviceItem.details.rejectionReason === 'Automatically rejected due to change in delivery address affecting the ZIP code qualification for short haul / line haul.' ) { @@ -215,7 +234,9 @@ const ServiceItemsTable = ({
{serviceItem.serviceItem} - {(code === 'DCRT' || code === 'ICRT') && serviceItem.details.standaloneCrate && ' - Standalone'} + {(code === SERVICE_ITEM_CODES.DCRT || code === SERVICE_ITEM_CODES.ICRT) && + serviceItem.details.standaloneCrate && + ' - Standalone'} {ALLOWED_RESUBMISSION_SI_CODES.includes(code) && resubmittedToolTip.isResubmitted ? ( { - if (code === 'DDFSIT' || code === 'DOFSIT' || code === 'IDFSIT' || code === 'IOFSIT') { + if ( + code === SERVICE_ITEM_CODES.DDFSIT || + code === SERVICE_ITEM_CODES.DOFSIT || + code === SERVICE_ITEM_CODES.IDFSIT || + code === SERVICE_ITEM_CODES.IOFSIT + ) { handleShowEditSitEntryDateModal(id, mtoShipmentID); } else { handleShowEditSitAddressModal(id, mtoShipmentID); From c6a8b88e3b817f2033003e2e775e9416b83a9596 Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Fri, 31 Jan 2025 19:55:06 +0000 Subject: [PATCH 09/26] remove duplicate code per PR --- .../ServiceItemDetails/ServiceItemDetails.jsx | 36 +------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx b/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx index a50fe5e03f5..f3ffcd1f16a 100644 --- a/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx +++ b/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx @@ -344,41 +344,7 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s break; } case SERVICE_ITEM_CODES.DOPSIT: - case SERVICE_ITEM_CODES.IOPSIT: { - detailSection = ( -
-
- {generateDetailText( - { - 'Original Pickup Address': details.sitOriginHHGOriginalAddress - ? formatCityStateAndPostalCode(details.sitOriginHHGOriginalAddress) - : '-', - 'Actual Pickup Address': details.sitOriginHHGActualAddress - ? formatCityStateAndPostalCode(details.sitOriginHHGActualAddress) - : '-', - 'Delivery miles into SIT': details.sitDeliveryMiles ? details.sitDeliveryMiles : '-', - }, - id, - )} - {details.rejectionReason && - generateDetailText({ 'Rejection reason': details.rejectionReason }, id, 'margin-top-2')} - {!isEmpty(serviceRequestDocUploads) ? ( -
-

Download service item documentation:

- {serviceRequestDocUploads.map((file) => ( - - ))} -
- ) : null} -
-
- ); - break; - } + case SERVICE_ITEM_CODES.IOPSIT: case SERVICE_ITEM_CODES.DOSFSC: case SERVICE_ITEM_CODES.IOSFSC: { detailSection = ( From dd11a7b351536e93e835e38c380b403ca67a495e Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Fri, 31 Jan 2025 20:02:26 +0000 Subject: [PATCH 10/26] consolidating/deleting per PR --- .../ServiceItemDetails/ServiceItemDetails.jsx | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx b/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx index f3ffcd1f16a..a8518a7bcc7 100644 --- a/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx +++ b/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx @@ -384,29 +384,9 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s case SERVICE_ITEM_CODES.DDFSIT: case SERVICE_ITEM_CODES.DDASIT: case SERVICE_ITEM_CODES.IDFSIT: - case SERVICE_ITEM_CODES.IDASIT: { - detailSection = generateDestinationSITDetailSection( - id, - serviceRequestDocUploads, - details, - code, - shipment, - sitStatus, - ); - break; - } + case SERVICE_ITEM_CODES.IDASIT: case SERVICE_ITEM_CODES.DDDSIT: - case SERVICE_ITEM_CODES.IDDSIT: { - detailSection = generateDestinationSITDetailSection( - id, - serviceRequestDocUploads, - details, - code, - shipment, - sitStatus, - ); - break; - } + case SERVICE_ITEM_CODES.IDDSIT: case SERVICE_ITEM_CODES.DDSFSC: case SERVICE_ITEM_CODES.IDSFSC: { detailSection = generateDestinationSITDetailSection( From d65fc26398bf37264d114a08e70078f3324e3d58 Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Fri, 31 Jan 2025 20:28:31 +0000 Subject: [PATCH 11/26] code cleanup --- .../Office/ServiceItemDetails/ServiceItemDetails.jsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx b/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx index a8518a7bcc7..b77ce920e78 100644 --- a/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx +++ b/src/components/Office/ServiceItemDetails/ServiceItemDetails.jsx @@ -39,8 +39,7 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai 'First available delivery date 1': '-', 'Customer contact 1': '-', }); - const numberOfDaysApprovedForDOASIT = shipment.sitDaysAllowance ? shipment.sitDaysAllowance - 1 : 0; - const numberOfDaysApprovedForIOASIT = shipment.sitDaysAllowance ? shipment.sitDaysAllowance - 1 : 0; + const numberOfDaysApprovedForSIT = shipment.sitDaysAllowance ? shipment.sitDaysAllowance - 1 : 0; const sitEndDate = sitStatus && sitStatus.currentSIT?.sitAuthorizedEndDate && @@ -70,7 +69,7 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai "Add'l SIT Start Date": details.sitEntryDate ? moment.utc(details.sitEntryDate).add(1, 'days').format('DD MMM YYYY') : '-', - '# of days approved for': shipment.sitDaysAllowance ? `${numberOfDaysApprovedForDOASIT} days` : '-', + '# of days approved for': shipment.sitDaysAllowance ? `${numberOfDaysApprovedForSIT} days` : '-', 'SIT expiration date': sitEndDate || '-', }, id, @@ -99,7 +98,7 @@ const generateDestinationSITDetailSection = (id, serviceRequestDocUploads, detai "Add'l SIT Start Date": details.sitEntryDate ? moment.utc(details.sitEntryDate).add(1, 'days').format('DD MMM YYYY') : '-', - '# of days approved for': shipment.sitDaysAllowance ? `${numberOfDaysApprovedForIOASIT} days` : '-', + '# of days approved for': shipment.sitDaysAllowance ? `${numberOfDaysApprovedForSIT} days` : '-', 'SIT expiration date': sitEndDate || '-', }, id, @@ -293,11 +292,11 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s } case SERVICE_ITEM_CODES.DOASIT: case SERVICE_ITEM_CODES.IOASIT: { - const numberOfDaysApprovedForIOASIT = shipment.sitDaysAllowance ? shipment.sitDaysAllowance - 1 : 0; const sitEndDate = sitStatus && sitStatus.currentSIT?.sitAuthorizedEndDate && formatDateWithUTC(sitStatus.currentSIT.sitAuthorizedEndDate, 'DD MMM YYYY'); + const numberOfDaysApprovedForSIT = shipment.sitDaysAllowance ? shipment.sitDaysAllowance - 1 : 0; detailSection = (
@@ -310,7 +309,7 @@ const ServiceItemDetails = ({ id, code, details, serviceRequestDocs, shipment, s "Add'l SIT Start Date": details.sitEntryDate ? moment.utc(details.sitEntryDate).add(1, 'days').format('DD MMM YYYY') : '-', - '# of days approved for': shipment.sitDaysAllowance ? `${numberOfDaysApprovedForIOASIT} days` : '-', + '# of days approved for': shipment.sitDaysAllowance ? `${numberOfDaysApprovedForSIT} days` : '-', 'SIT expiration date': sitEndDate || '-', 'Customer contacted homesafe': details.sitCustomerContacted ? formatDateWithUTC(details.sitCustomerContacted, 'DD MMM YYYY') From 959e2585f8760a6987197d8bb10dc6344e545b9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 19:16:12 +0000 Subject: [PATCH 12/26] Bump store2 from 2.14.2 to 2.14.4 Bumps [store2](https://github.com/nbubna/store) from 2.14.2 to 2.14.4. - [Commits](https://github.com/nbubna/store/compare/2.14.2...2.14.4) --- updated-dependencies: - dependency-name: store2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c38785ccc74..3323c312234 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16298,9 +16298,9 @@ stop-iteration-iterator@^1.0.0: internal-slot "^1.0.4" store2@^2.14.2: - version "2.14.2" - resolved "https://registry.yarnpkg.com/store2/-/store2-2.14.2.tgz#56138d200f9fe5f582ad63bc2704dbc0e4a45068" - integrity sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w== + version "2.14.4" + resolved "https://registry.yarnpkg.com/store2/-/store2-2.14.4.tgz#81b313abaddade4dcd7570c5cc0e3264a8f7a242" + integrity sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw== storybook@^7.6.20: version "7.6.20" From dda6ab58cfcfa2fecd515e6935b3359b54f183a7 Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Tue, 4 Feb 2025 20:52:18 +0000 Subject: [PATCH 13/26] Fix comments per PR review --- pkg/handlers/primeapi/payloads/payload_to_model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/handlers/primeapi/payloads/payload_to_model.go b/pkg/handlers/primeapi/payloads/payload_to_model.go index 7de1066f70a..c3e352c9336 100644 --- a/pkg/handlers/primeapi/payloads/payload_to_model.go +++ b/pkg/handlers/primeapi/payloads/payload_to_model.go @@ -479,7 +479,7 @@ func MTOServiceItemModel(mtoServiceItem primemessages.MTOServiceItem) (*models.M } model.Reason = originsit.Reason - // Check for reason required field on a DDFSIT + // Check for reason required field on a IOASIT if model.ReService.Code == models.ReServiceCodeIOASIT { reasonVerrs := validateReasonInternationalOriginSIT(*originsit) From 6651098cdd7e8c15747029dded892fcb1b80a97b Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Wed, 5 Feb 2025 18:47:20 +0000 Subject: [PATCH 14/26] fix comments --- .../mto_service_item/mto_service_item_validators_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/mto_service_item/mto_service_item_validators_test.go b/pkg/services/mto_service_item/mto_service_item_validators_test.go index ca918f93650..95450a89c90 100644 --- a/pkg/services/mto_service_item/mto_service_item_validators_test.go +++ b/pkg/services/mto_service_item/mto_service_item_validators_test.go @@ -231,7 +231,7 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemData() { oldServiceItem, newServiceItem := setupTestData() // Create old and new service item - // Make both service items of type DOFSIT because this type of service item will be checked by checkForSITItemChanges + // Make both service items of type IOFSIT because this type of service item will be checked by checkForSITItemChanges oldServiceItem.ReService.Code = models.ReServiceCodeIOFSIT newServiceItem.ReService.Code = models.ReServiceCodeIOFSIT oldServiceItem.SITDepartureDate, newServiceItem.SITDepartureDate = &now, &now From 1c148c7a4aa342f009d913159d4a9dcf69b942f7 Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Wed, 12 Feb 2025 17:52:29 +0000 Subject: [PATCH 15/26] intitial checkin of international SIT address change --- .../mto_service_item_updater.go | 18 +- .../mto_service_item_updater_test.go | 314 ++++++++++++++++++ .../mto_shipment/mto_shipment_updater.go | 7 +- .../shipment_address_update_requester.go | 18 +- .../shipment_address_update_requester_test.go | 84 +++++ 5 files changed, 432 insertions(+), 9 deletions(-) diff --git a/pkg/services/mto_service_item/mto_service_item_updater.go b/pkg/services/mto_service_item/mto_service_item_updater.go index 3c17ceff042..503f2efe440 100644 --- a/pkg/services/mto_service_item/mto_service_item_updater.go +++ b/pkg/services/mto_service_item/mto_service_item_updater.go @@ -239,7 +239,11 @@ func (p *mtoServiceItemUpdater) updateServiceItem(appCtx appcontext.AppContext, if (serviceItem.ReService.Code == models.ReServiceCodeDDDSIT || serviceItem.ReService.Code == models.ReServiceCodeDDSFSC || serviceItem.ReService.Code == models.ReServiceCodeDDASIT || - serviceItem.ReService.Code == models.ReServiceCodeDDFSIT) && + serviceItem.ReService.Code == models.ReServiceCodeDDFSIT || + serviceItem.ReService.Code == models.ReServiceCodeIDDSIT || + serviceItem.ReService.Code == models.ReServiceCodeIDSFSC || + serviceItem.ReService.Code == models.ReServiceCodeIDASIT || + serviceItem.ReService.Code == models.ReServiceCodeIDFSIT) && serviceItem.SITDestinationOriginalAddressID == nil { // Set the original address on a service item to the shipment's @@ -267,9 +271,11 @@ func (p *mtoServiceItemUpdater) updateServiceItem(appCtx appcontext.AppContext, serviceItem.SITDestinationFinalAddress = shipmentDestinationAddress } - // Calculate SITDeliveryMiles for DDDSIT and DDSFSC origin SIT service items + // Calculate SITDeliveryMiles for DDDSIT/DDSFSC, IDDSIT/IDSFSC origin SIT service items if serviceItem.ReService.Code == models.ReServiceCodeDDDSIT || - serviceItem.ReService.Code == models.ReServiceCodeDDSFSC { + serviceItem.ReService.Code == models.ReServiceCodeDDSFSC || + serviceItem.ReService.Code == models.ReServiceCodeIDDSIT || + serviceItem.ReService.Code == models.ReServiceCodeIDSFSC { // Destination SIT: distance between shipment destination address & service item ORIGINAL destination address milesCalculated, err := p.planner.ZipTransitDistance(appCtx, mtoShipment.DestinationAddress.PostalCode, serviceItem.SITDestinationOriginalAddress.PostalCode, false) if err != nil { @@ -279,9 +285,11 @@ func (p *mtoServiceItemUpdater) updateServiceItem(appCtx appcontext.AppContext, } } - // Calculate SITDeliveryMiles for DOPSIT and DOSFSC origin SIT service items + // Calculate SITDeliveryMiles for DOPSIT/DOSFSC, IOPSIT/IOSFSC origin SIT service items if serviceItem.ReService.Code == models.ReServiceCodeDOPSIT || - serviceItem.ReService.Code == models.ReServiceCodeDOSFSC { + serviceItem.ReService.Code == models.ReServiceCodeDOSFSC || + serviceItem.ReService.Code == models.ReServiceCodeIOPSIT || + serviceItem.ReService.Code == models.ReServiceCodeIOSFSC { // Origin SIT: distance between shipment pickup address & service item ORIGINAL pickup address milesCalculated, err := p.planner.ZipTransitDistance(appCtx, mtoShipment.PickupAddress.PostalCode, serviceItem.SITOriginHHGOriginalAddress.PostalCode, false) if err != nil { diff --git a/pkg/services/mto_service_item/mto_service_item_updater_test.go b/pkg/services/mto_service_item/mto_service_item_updater_test.go index 95f5191681a..58915d045d1 100644 --- a/pkg/services/mto_service_item/mto_service_item_updater_test.go +++ b/pkg/services/mto_service_item/mto_service_item_updater_test.go @@ -765,6 +765,14 @@ func (suite *MTOServiceItemServiceSuite) TestMTOServiceItemUpdater() { models.ReServiceCodeDOPSIT, models.ReServiceCodeDOFSIT, models.ReServiceCodeDOSFSC, + models.ReServiceCodeIDFSIT, + models.ReServiceCodeIDASIT, + models.ReServiceCodeIDDSIT, + models.ReServiceCodeIDSFSC, + models.ReServiceCodeIOASIT, + models.ReServiceCodeIOPSIT, + models.ReServiceCodeIOFSIT, + models.ReServiceCodeIOSFSC, } shipmentSITAllowance := 90 @@ -2389,6 +2397,68 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemStatus() { suite.Equal(destinationAddress.PostalCode, updatedServiceItem.SITDestinationOriginalAddress.PostalCode) }) + suite.Run("When TOO approves a IDSFSC service item with an existing SITDestinationFinalAddress", func() { + move := factory.BuildApprovalsRequestedMove(suite.DB(), nil, nil) + destUSPRC, _ := models.FindByZipCode(suite.AppContextForTest().DB(), "99505") + sitDestinationFinalAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "JBER", + City: "Anchorage", + State: "AK", + PostalCode: "99505", + IsOconus: models.BoolPointer(true), + UsPostRegionCityID: &destUSPRC.ID, + }, + }, + }, nil) + + serviceItem := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + { + Model: models.ReService{ + Code: models.ReServiceCodeIDSFSC, + }, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + { + Model: sitDestinationFinalAddress, + LinkOnly: true, + Type: &factory.Addresses.SITDestinationFinalAddress, + }, + }, nil) + + eTag := etag.GenerateEtag(serviceItem.UpdatedAt) + + updatedServiceItem, err := updater.ApproveOrRejectServiceItem( + suite.AppContextForTest(), serviceItem.ID, models.MTOServiceItemStatusApproved, rejectionReason, eTag) + suite.NoError(err) + + // ApproveOrRejectServiceItem doesn't return the service item with the updated move + // get move from the db to check the updated status + err = suite.DB().Find(&move, move.ID) + suite.NoError(err) + suite.Equal(models.MoveStatusAPPROVED, move.Status) + + suite.Equal(models.MTOServiceItemStatusApproved, updatedServiceItem.Status) + suite.NotNil(updatedServiceItem.ApprovedAt) + suite.Nil(updatedServiceItem.RejectionReason) + suite.Nil(updatedServiceItem.RejectedAt) + suite.NotNil(updatedServiceItem) + destinationAddress := serviceItem.MTOShipment.DestinationAddress + suite.Equal(destinationAddress.StreetAddress1, updatedServiceItem.SITDestinationOriginalAddress.StreetAddress1) + suite.Equal(destinationAddress.City, updatedServiceItem.SITDestinationOriginalAddress.City) + suite.Equal(destinationAddress.State, updatedServiceItem.SITDestinationOriginalAddress.State) + suite.Equal(destinationAddress.PostalCode, updatedServiceItem.SITDestinationOriginalAddress.PostalCode) + }) + suite.Run("When TOO approves a DDSFSC service item without a SITDestinationFinalAddress", func() { move := factory.BuildApprovalsRequestedMove(suite.DB(), nil, nil) shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ @@ -2488,6 +2558,55 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemStatus() { suite.Equal(destinationAddress.PostalCode, updatedServiceItem.SITDestinationOriginalAddress.PostalCode) }) + suite.Run("When TOO approves a IDASIT service item with an existing SITDestinationFinalAddress", func() { + move := factory.BuildApprovalsRequestedMove(suite.DB(), nil, nil) + sitDestinationFinalAddress := factory.BuildAddress(suite.DB(), nil, nil) + serviceItem := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + { + Model: models.ReService{ + Code: models.ReServiceCodeIDASIT, + }, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + { + Model: sitDestinationFinalAddress, + LinkOnly: true, + Type: &factory.Addresses.SITDestinationFinalAddress, + }, + }, nil) + + eTag := etag.GenerateEtag(serviceItem.UpdatedAt) + + updatedServiceItem, err := updater.ApproveOrRejectServiceItem( + suite.AppContextForTest(), serviceItem.ID, models.MTOServiceItemStatusApproved, rejectionReason, eTag) + suite.NoError(err) + + // ApproveOrRejectServiceItem doesn't return the service item with the updated move + // get move from the db to check the updated status + err = suite.DB().Find(&move, move.ID) + suite.NoError(err) + suite.Equal(models.MoveStatusAPPROVED, move.Status) + + suite.Equal(models.MTOServiceItemStatusApproved, updatedServiceItem.Status) + suite.NotNil(updatedServiceItem.ApprovedAt) + suite.Nil(updatedServiceItem.RejectionReason) + suite.Nil(updatedServiceItem.RejectedAt) + suite.NotNil(updatedServiceItem) + destinationAddress := serviceItem.MTOShipment.DestinationAddress + suite.Equal(destinationAddress.StreetAddress1, updatedServiceItem.SITDestinationOriginalAddress.StreetAddress1) + suite.Equal(destinationAddress.City, updatedServiceItem.SITDestinationOriginalAddress.City) + suite.Equal(destinationAddress.State, updatedServiceItem.SITDestinationOriginalAddress.State) + suite.Equal(destinationAddress.PostalCode, updatedServiceItem.SITDestinationOriginalAddress.PostalCode) + }) + suite.Run("When TOO approves a DDASIT service item without a SITDestinationFinalAddress", func() { move := factory.BuildApprovalsRequestedMove(suite.DB(), nil, nil) shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ @@ -2538,6 +2657,56 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemStatus() { suite.Equal(shipment.DestinationAddress.PostalCode, updatedServiceItem.SITDestinationOriginalAddress.PostalCode) }) + suite.Run("When TOO approves a IDASIT service item without a SITDestinationFinalAddress", func() { + move := factory.BuildApprovalsRequestedMove(suite.DB(), nil, nil) + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + }, nil) + serviceItem := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: shipment, + LinkOnly: true, + }, + { + Model: models.ReService{ + Code: models.ReServiceCodeIDASIT, + }, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + }, nil) + + eTag := etag.GenerateEtag(serviceItem.UpdatedAt) + + updatedServiceItem, err := updater.ApproveOrRejectServiceItem( + suite.AppContextForTest(), serviceItem.ID, models.MTOServiceItemStatusApproved, rejectionReason, eTag) + suite.NoError(err) + + // ApproveOrRejectServiceItem doesn't return the service item with the updated move + // get move from the db to check the updated status + err = suite.DB().Find(&move, move.ID) + suite.NoError(err) + suite.Equal(models.MoveStatusAPPROVED, move.Status) + + suite.Equal(models.MTOServiceItemStatusApproved, updatedServiceItem.Status) + suite.NotNil(updatedServiceItem.ApprovedAt) + suite.Nil(updatedServiceItem.RejectionReason) + suite.Nil(updatedServiceItem.RejectedAt) + suite.NotNil(updatedServiceItem) + suite.NotEqual(shipment.DestinationAddressID, *updatedServiceItem.SITDestinationOriginalAddressID) + suite.NotEqual(shipment.DestinationAddress.ID, *updatedServiceItem.SITDestinationOriginalAddressID) + suite.Equal(shipment.DestinationAddress.StreetAddress1, updatedServiceItem.SITDestinationOriginalAddress.StreetAddress1) + suite.Equal(shipment.DestinationAddress.City, updatedServiceItem.SITDestinationOriginalAddress.City) + suite.Equal(shipment.DestinationAddress.State, updatedServiceItem.SITDestinationOriginalAddress.State) + suite.Equal(shipment.DestinationAddress.PostalCode, updatedServiceItem.SITDestinationOriginalAddress.PostalCode) + }) + suite.Run("When TOO approves a DDFSIT service item with an existing SITDestinationFinalAddress", func() { move := factory.BuildApprovalsRequestedMove(suite.DB(), nil, nil) sitDestinationFinalAddress := factory.BuildAddress(suite.DB(), nil, nil) @@ -2587,6 +2756,55 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemStatus() { suite.Equal(destinationAddress.PostalCode, updatedServiceItem.SITDestinationOriginalAddress.PostalCode) }) + suite.Run("When TOO approves a IDFSIT service item with an existing SITDestinationFinalAddress", func() { + move := factory.BuildApprovalsRequestedMove(suite.DB(), nil, nil) + sitDestinationFinalAddress := factory.BuildAddress(suite.DB(), nil, nil) + serviceItem := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + { + Model: models.ReService{ + Code: models.ReServiceCodeIDFSIT, + }, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + { + Model: sitDestinationFinalAddress, + LinkOnly: true, + Type: &factory.Addresses.SITDestinationFinalAddress, + }, + }, nil) + + eTag := etag.GenerateEtag(serviceItem.UpdatedAt) + + updatedServiceItem, err := updater.ApproveOrRejectServiceItem( + suite.AppContextForTest(), serviceItem.ID, models.MTOServiceItemStatusApproved, rejectionReason, eTag) + suite.NoError(err) + + // ApproveOrRejectServiceItem doesn't return the service item with the updated move + // get move from the db to check the updated status + err = suite.DB().Find(&move, move.ID) + suite.NoError(err) + suite.Equal(models.MoveStatusAPPROVED, move.Status) + + suite.Equal(models.MTOServiceItemStatusApproved, updatedServiceItem.Status) + suite.NotNil(updatedServiceItem.ApprovedAt) + suite.Nil(updatedServiceItem.RejectionReason) + suite.Nil(updatedServiceItem.RejectedAt) + suite.NotNil(updatedServiceItem) + destinationAddress := serviceItem.MTOShipment.DestinationAddress + suite.Equal(destinationAddress.StreetAddress1, updatedServiceItem.SITDestinationOriginalAddress.StreetAddress1) + suite.Equal(destinationAddress.City, updatedServiceItem.SITDestinationOriginalAddress.City) + suite.Equal(destinationAddress.State, updatedServiceItem.SITDestinationOriginalAddress.State) + suite.Equal(destinationAddress.PostalCode, updatedServiceItem.SITDestinationOriginalAddress.PostalCode) + }) + suite.Run("When TOO approves a DDFSIT service item without a SITDestinationFinalAddress", func() { move := factory.BuildApprovalsRequestedMove(suite.DB(), nil, nil) shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ @@ -2637,6 +2855,56 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemStatus() { suite.Equal(shipment.DestinationAddress.PostalCode, updatedServiceItem.SITDestinationOriginalAddress.PostalCode) }) + suite.Run("When TOO approves a IDFSIT service item without a SITDestinationFinalAddress", func() { + move := factory.BuildApprovalsRequestedMove(suite.DB(), nil, nil) + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + }, nil) + serviceItem := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: shipment, + LinkOnly: true, + }, + { + Model: models.ReService{ + Code: models.ReServiceCodeIDFSIT, + }, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusSubmitted, + }, + }, + }, nil) + + eTag := etag.GenerateEtag(serviceItem.UpdatedAt) + + updatedServiceItem, err := updater.ApproveOrRejectServiceItem( + suite.AppContextForTest(), serviceItem.ID, models.MTOServiceItemStatusApproved, rejectionReason, eTag) + suite.NoError(err) + + // ApproveOrRejectServiceItem doesn't return the service item with the updated move + // get move from the db to check the updated status + err = suite.DB().Find(&move, move.ID) + suite.NoError(err) + suite.Equal(models.MoveStatusAPPROVED, move.Status) + + suite.Equal(models.MTOServiceItemStatusApproved, updatedServiceItem.Status) + suite.NotNil(updatedServiceItem.ApprovedAt) + suite.Nil(updatedServiceItem.RejectionReason) + suite.Nil(updatedServiceItem.RejectedAt) + suite.NotNil(updatedServiceItem) + suite.NotEqual(shipment.DestinationAddressID, *updatedServiceItem.SITDestinationOriginalAddressID) + suite.NotEqual(shipment.DestinationAddress.ID, *updatedServiceItem.SITDestinationOriginalAddressID) + suite.Equal(shipment.DestinationAddress.StreetAddress1, updatedServiceItem.SITDestinationOriginalAddress.StreetAddress1) + suite.Equal(shipment.DestinationAddress.City, updatedServiceItem.SITDestinationOriginalAddress.City) + suite.Equal(shipment.DestinationAddress.State, updatedServiceItem.SITDestinationOriginalAddress.State) + suite.Equal(shipment.DestinationAddress.PostalCode, updatedServiceItem.SITDestinationOriginalAddress.PostalCode) + }) + // Test that the move's status changes to Approvals Requested if any of its service // items' status is SUBMITTED suite.Run("When move is approved and service item is submitted", func() { @@ -2784,6 +3052,52 @@ func (suite *MTOServiceItemServiceSuite) TestUpdateMTOServiceItemStatus() { suite.NotNil(updatedServiceItem) }) + suite.Run("When TOO rejects a IOFSIT service item and converts it to the customer expense", func() { + move := factory.BuildApprovalsRequestedMove(suite.DB(), nil, nil) + serviceItem := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + { + Model: models.ReService{ + Code: models.ReServiceCodeIOFSIT, + }, + }, + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusApproved, + }, + }, + { + Model: models.Address{}, + Type: &factory.Addresses.SITOriginHHGActualAddress, + }, + { + Model: models.Address{}, + Type: &factory.Addresses.SITOriginHHGOriginalAddress, + }, + }, nil) + + eTag := etag.GenerateEtag(serviceItem.UpdatedAt) + + updatedServiceItem, err := updater.ApproveOrRejectServiceItem( + suite.AppContextForTest(), serviceItem.ID, models.MTOServiceItemStatusApproved, rejectionReason, eTag) + suite.NoError(err) + + // ApproveOrRejectServiceItem doesn't return the service item with the updated move + // get move from the db to check the updated status + err = suite.DB().Find(&move, move.ID) + suite.NoError(err) + suite.Equal(models.MoveStatusAPPROVED, move.Status) + + suite.Equal(models.MTOServiceItemStatusApproved, updatedServiceItem.Status) + suite.NotNil(updatedServiceItem.ApprovedAt) + suite.Nil(updatedServiceItem.RejectionReason) + suite.Nil(updatedServiceItem.RejectedAt) + suite.NotNil(updatedServiceItem) + }) + suite.Run("Returns a not found error if the updater can't find the ReService code for DOFSIT in the DB.", func() { _, err := updater.ConvertItemToCustomerExpense( suite.AppContextForTest(), &models.MTOShipment{}, models.StringPointer("test"), true) diff --git a/pkg/services/mto_shipment/mto_shipment_updater.go b/pkg/services/mto_shipment/mto_shipment_updater.go index fb75c795a77..fdc31739d46 100644 --- a/pkg/services/mto_shipment/mto_shipment_updater.go +++ b/pkg/services/mto_shipment/mto_shipment_updater.go @@ -1307,7 +1307,8 @@ func UpdateDestinationSITServiceItemsAddress(appCtx appcontext.AppContext, shipm mtoServiceItems := mtoShipment.MTOServiceItems // Only update these serviceItems address ID - serviceItemsToUpdate := []models.ReServiceCode{models.ReServiceCodeDDDSIT, models.ReServiceCodeDDFSIT, models.ReServiceCodeDDASIT, models.ReServiceCodeDDSFSC} + serviceItemsToUpdate := []models.ReServiceCode{models.ReServiceCodeDDDSIT, models.ReServiceCodeDDFSIT, models.ReServiceCodeDDASIT, models.ReServiceCodeDDSFSC, + models.ReServiceCodeIDDSIT, models.ReServiceCodeIDFSIT, models.ReServiceCodeIDASIT, models.ReServiceCodeIDSFSC} for _, serviceItem := range mtoServiceItems { @@ -1350,7 +1351,9 @@ func UpdateDestinationSITServiceItemsSITDeliveryMiles(planner route.Planner, app serviceItem := s reServiceCode := serviceItem.ReService.Code if reServiceCode == models.ReServiceCodeDDDSIT || - reServiceCode == models.ReServiceCodeDDSFSC { + reServiceCode == models.ReServiceCodeDDSFSC || + reServiceCode == models.ReServiceCodeIDDSIT || + reServiceCode == models.ReServiceCodeIDSFSC { var milesCalculated int diff --git a/pkg/services/shipment_address_update/shipment_address_update_requester.go b/pkg/services/shipment_address_update/shipment_address_update_requester.go index 1c2e93b198c..73273149ffc 100644 --- a/pkg/services/shipment_address_update/shipment_address_update_requester.go +++ b/pkg/services/shipment_address_update/shipment_address_update_requester.go @@ -178,7 +178,14 @@ func (f *shipmentAddressUpdateRequester) doesShipmentContainApprovedDestinationS for _, serviceItem := range serviceItems { serviceCode := serviceItem.ReService.Code status := serviceItem.Status - if (serviceCode == models.ReServiceCodeDDASIT || serviceCode == models.ReServiceCodeDDDSIT || serviceCode == models.ReServiceCodeDDFSIT || serviceCode == models.ReServiceCodeDDSFSC) && + if (serviceCode == models.ReServiceCodeDDASIT || + serviceCode == models.ReServiceCodeDDDSIT || + serviceCode == models.ReServiceCodeDDFSIT || + serviceCode == models.ReServiceCodeDDSFSC || + serviceCode == models.ReServiceCodeIDASIT || + serviceCode == models.ReServiceCodeIDDSIT || + serviceCode == models.ReServiceCodeIDFSIT || + serviceCode == models.ReServiceCodeIDSFSC) && status == models.MTOServiceItemStatusApproved { return true } @@ -316,7 +323,14 @@ func (f *shipmentAddressUpdateRequester) RequestShipmentDeliveryAddressUpdate(ap serviceItems := shipment.MTOServiceItems for _, serviceItem := range serviceItems { serviceCode := serviceItem.ReService.Code - if serviceCode == models.ReServiceCodeDDASIT || serviceCode == models.ReServiceCodeDDDSIT || serviceCode == models.ReServiceCodeDDFSIT || serviceCode == models.ReServiceCodeDDSFSC { + if serviceCode == models.ReServiceCodeDDASIT || + serviceCode == models.ReServiceCodeDDDSIT || + serviceCode == models.ReServiceCodeDDFSIT || + serviceCode == models.ReServiceCodeDDSFSC || + serviceCode == models.ReServiceCodeIDASIT || + serviceCode == models.ReServiceCodeIDDSIT || + serviceCode == models.ReServiceCodeIDFSIT || + serviceCode == models.ReServiceCodeIDSFSC { if serviceItem.SITDestinationOriginalAddressID != nil { addressUpdate.SitOriginalAddressID = serviceItem.SITDestinationOriginalAddressID } diff --git a/pkg/services/shipment_address_update/shipment_address_update_requester_test.go b/pkg/services/shipment_address_update/shipment_address_update_requester_test.go index 223cf820988..354ce36dd56 100644 --- a/pkg/services/shipment_address_update/shipment_address_update_requester_test.go +++ b/pkg/services/shipment_address_update/shipment_address_update_requester_test.go @@ -744,6 +744,90 @@ func (suite *ShipmentAddressUpdateServiceSuite) TestCreateApprovedShipmentAddres suite.Equal(*addressUpdate.OldSitDistanceBetween, 0) suite.Equal(*addressUpdate.SitOriginalAddressID, *serviceItemDDASIT.SITDestinationOriginalAddressID) }) + + suite.Run("destination address request succeeds when containing international destination SIT", func() { + move := setupTestData() + newAddress := models.Address{ + StreetAddress1: "123 Any St", + City: "Anchorage", + State: "AK", + PostalCode: "99695", + } + + setupInternationalSITCodes := []models.ReServiceCode{ + models.ReServiceCodeIDASIT, + models.ReServiceCodeIDDSIT, + models.ReServiceCodeIDFSIT, + models.ReServiceCodeIDSFSC, + } + + // loop through test codes to verify updates are applied for expected international SITs + for _, reServiceCode := range setupInternationalSITCodes { + shipment := factory.BuildMTOShipmentWithMove(&move, suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeInternational, + }, + }, + }, nil) + + // building service item to get dest SIT checks + serviceItemDDASIT := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.MTOServiceItem{ + Status: models.MTOServiceItemStatusApproved, + SITDestinationOriginalAddressID: shipment.DestinationAddressID, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: shipment, + LinkOnly: true, + }, + { + Model: models.ReService{ + Code: reServiceCode, + }, + }, + }, nil) + + // mock ZipTransitDistance function + mockPlanner.On("ZipTransitDistance", + mock.AnythingOfType("*appcontext.appContext"), + "94535", + "94535", + false, + ).Return(0, nil).Once() + mockPlanner.On("ZipTransitDistance", + mock.AnythingOfType("*appcontext.appContext"), + "94523", + "99695", + false, + ).Return(500, nil).Once() + mockPlanner.On("ZipTransitDistance", + mock.AnythingOfType("*appcontext.appContext"), + "94535", + "99695", + false, + ).Return(1000, nil).Once() + + // request the update + update, err := addressUpdateRequester.RequestShipmentDeliveryAddressUpdate(suite.AppContextForTest(), shipment.ID, newAddress, "we really need to change the address", etag.GenerateEtag(shipment.UpdatedAt)) + suite.NoError(err) + suite.NotNil(update) + + // querying the address update to make sure that SIT data was populated + var addressUpdate models.ShipmentAddressUpdate + err = suite.DB().Find(&addressUpdate, update.ID) + suite.NoError(err) + suite.Equal(*addressUpdate.NewSitDistanceBetween, 1000) + suite.Equal(*addressUpdate.OldSitDistanceBetween, 0) + suite.Equal(*addressUpdate.SitOriginalAddressID, *serviceItemDDASIT.SITDestinationOriginalAddressID) + } + }) } func (suite *ShipmentAddressUpdateServiceSuite) TestTOOApprovedShipmentAddressUpdateRequest() { From 5ed03cb2c21fb1fe77a87146f356f0b8d68a6c92 Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Wed, 12 Feb 2025 19:37:47 +0000 Subject: [PATCH 16/26] add validation to prevent international SITs to be created for domestic shipments --- .../mto_service_item_creator.go | 23 ++++ .../mto_service_item_creator_test.go | 116 ++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/pkg/services/mto_service_item/mto_service_item_creator.go b/pkg/services/mto_service_item/mto_service_item_creator.go index a88d644c94b..3f6db514039 100644 --- a/pkg/services/mto_service_item/mto_service_item_creator.go +++ b/pkg/services/mto_service_item/mto_service_item_creator.go @@ -3,6 +3,7 @@ package mtoserviceitem import ( "database/sql" "fmt" + "slices" "strconv" "time" @@ -708,6 +709,10 @@ func (o *mtoServiceItemCreator) CreateMTOServiceItem(appCtx appcontext.AppContex return err } } else { + if isInternationalServiceItem(requestedServiceItem) { + err := fmt.Errorf("cannot create international service items for domestic shipment: %s", mtoShipment.ID) + return apperror.NewInvalidInputError(mtoShipment.ID, err, nil, err.Error()) + } verrs, err = o.builder.CreateOne(txnAppCtx, requestedServiceItem) if verrs != nil || err != nil { return fmt.Errorf("%#v %e", verrs, err) @@ -986,3 +991,21 @@ func (o *mtoServiceItemCreator) validateFirstDaySITServiceItem(appCtx appcontext return &extraServiceItems, nil } + +func isInternationalServiceItem(serviceItem *models.MTOServiceItem) bool { + var internationalAccessorialServiceItems = []models.ReServiceCode{ + models.ReServiceCodeICRT, + models.ReServiceCodeIUCRT, + models.ReServiceCodeIOASIT, + models.ReServiceCodeIDASIT, + models.ReServiceCodeIOFSIT, + models.ReServiceCodeIDFSIT, + models.ReServiceCodeIOPSIT, + models.ReServiceCodeIDDSIT, + models.ReServiceCodeIDSHUT, + models.ReServiceCodeIOSHUT, + models.ReServiceCodeIOSFSC, + models.ReServiceCodeIDSFSC, + } + return slices.Contains(internationalAccessorialServiceItems, serviceItem.ReService.Code) +} diff --git a/pkg/services/mto_service_item/mto_service_item_creator_test.go b/pkg/services/mto_service_item/mto_service_item_creator_test.go index 66935f7b5e7..be8ffefc460 100644 --- a/pkg/services/mto_service_item/mto_service_item_creator_test.go +++ b/pkg/services/mto_service_item/mto_service_item_creator_test.go @@ -1920,4 +1920,120 @@ func (suite *MTOServiceItemServiceSuite) TestCreateDestSITServiceItem() { suite.NotEmpty(invalidInputError.ValidationErrors) suite.Contains(invalidInputError.ValidationErrors.Keys(), "reServiceCode") }) + + suite.Run("Failure - cannot create domestic service item international domestic shipment", func() { + move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) + dimension := models.MTOServiceItemDimension{ + Type: models.DimensionTypeItem, + Length: 12000, + Height: 12000, + Width: 12000, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + // setup domestic shipment + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeInternational, + }, + }, + }, nil) + destAddress := factory.BuildDefaultAddress(suite.DB()) + + // setup international service item. must fail validation for a domestic shipment + reServiceDDFSIT := factory.FetchReServiceByCode(suite.DB(), models.ReServiceCodeDDFSIT) + internationalServiceItem := models.MTOServiceItem{ + MoveTaskOrderID: move.ID, + MoveTaskOrder: move, + ReService: reServiceDDFSIT, + MTOShipmentID: &shipment.ID, + MTOShipment: shipment, + Dimensions: models.MTOServiceItemDimensions{dimension}, + Status: models.MTOServiceItemStatusSubmitted, + SITDestinationFinalAddressID: &destAddress.ID, + SITDestinationFinalAddress: &destAddress, + } + + builder := query.NewQueryBuilder() + moveRouter := moverouter.NewMoveRouter() + planner := &mocks.Planner{} + planner.On("ZipTransitDistance", + mock.AnythingOfType("*appcontext.appContext"), + mock.Anything, + mock.Anything, + false, + ).Return(400, nil) + creator := NewMTOServiceItemCreator(planner, builder, moveRouter, ghcrateengine.NewDomesticUnpackPricer(), ghcrateengine.NewDomesticPackPricer(), ghcrateengine.NewDomesticLinehaulPricer(), ghcrateengine.NewDomesticShorthaulPricer(), ghcrateengine.NewDomesticOriginPricer(), ghcrateengine.NewDomesticDestinationPricer(), ghcrateengine.NewFuelSurchargePricer()) + + createdServiceItems, _, err := creator.CreateMTOServiceItem(suite.AppContextForTest(), &internationalServiceItem) + suite.Nil(createdServiceItems) + suite.Error(err) + suite.IsType(apperror.InvalidInputError{}, err) + + suite.Contains(err.Error(), "cannot create domestic service items for international shipment") + }) + + suite.Run("Failure - cannot create international service item for domestic shipment", func() { + move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) + dimension := models.MTOServiceItemDimension{ + Type: models.DimensionTypeItem, + Length: 12000, + Height: 12000, + Width: 12000, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + // setup domestic shipment + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeDomestic, + }, + }, + }, nil) + destAddress := factory.BuildDefaultAddress(suite.DB()) + + // setup international service item. must fail validation for a domestic shipment + reServiceIDFSIT := factory.FetchReServiceByCode(suite.DB(), models.ReServiceCodeIDFSIT) + internationalServiceItem := models.MTOServiceItem{ + MoveTaskOrderID: move.ID, + MoveTaskOrder: move, + ReService: reServiceIDFSIT, + MTOShipmentID: &shipment.ID, + MTOShipment: shipment, + Dimensions: models.MTOServiceItemDimensions{dimension}, + Status: models.MTOServiceItemStatusSubmitted, + SITDestinationFinalAddressID: &destAddress.ID, + SITDestinationFinalAddress: &destAddress, + } + + builder := query.NewQueryBuilder() + moveRouter := moverouter.NewMoveRouter() + planner := &mocks.Planner{} + planner.On("ZipTransitDistance", + mock.AnythingOfType("*appcontext.appContext"), + mock.Anything, + mock.Anything, + false, + ).Return(400, nil) + creator := NewMTOServiceItemCreator(planner, builder, moveRouter, ghcrateengine.NewDomesticUnpackPricer(), ghcrateengine.NewDomesticPackPricer(), ghcrateengine.NewDomesticLinehaulPricer(), ghcrateengine.NewDomesticShorthaulPricer(), ghcrateengine.NewDomesticOriginPricer(), ghcrateengine.NewDomesticDestinationPricer(), ghcrateengine.NewFuelSurchargePricer()) + + createdServiceItems, _, err := creator.CreateMTOServiceItem(suite.AppContextForTest(), &internationalServiceItem) + suite.Nil(createdServiceItems) + suite.Error(err) + suite.IsType(apperror.InvalidInputError{}, err) + + suite.Contains(err.Error(), "cannot create international service items for domestic shipment") + }) } From 1db1383aeee51d419d5882eadbeafe5205808599 Mon Sep 17 00:00:00 2001 From: Michael Inthavongsay Date: Thu, 13 Feb 2025 00:56:40 +0000 Subject: [PATCH 17/26] Fix failing unit tests related to new validation --- pkg/handlers/primeapi/mto_service_item_test.go | 6 ++++++ pkg/handlers/primeapiv2/mto_service_item_test.go | 5 +++++ pkg/handlers/primeapiv3/mto_service_item_test.go | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/pkg/handlers/primeapi/mto_service_item_test.go b/pkg/handlers/primeapi/mto_service_item_test.go index cc57bed0057..f987915642a 100644 --- a/pkg/handlers/primeapi/mto_service_item_test.go +++ b/pkg/handlers/primeapi/mto_service_item_test.go @@ -257,7 +257,13 @@ func (suite *HandlerSuite) TestCreateMTOServiceItemHandler() { Model: mto, LinkOnly: true, }, + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeInternational, + }, + }, }, nil) + mtoShipment.PrimeEstimatedWeight = nil req := httptest.NewRequest("POST", "/mto-service-items", nil) reason := "lorem ipsum" diff --git a/pkg/handlers/primeapiv2/mto_service_item_test.go b/pkg/handlers/primeapiv2/mto_service_item_test.go index a48c9bfe87d..376353adfa4 100644 --- a/pkg/handlers/primeapiv2/mto_service_item_test.go +++ b/pkg/handlers/primeapiv2/mto_service_item_test.go @@ -194,6 +194,11 @@ func (suite *HandlerSuite) TestCreateMTOServiceItemHandler() { Model: mto, LinkOnly: true, }, + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeInternational, + }, + }, }, nil) mtoShipment.PrimeEstimatedWeight = nil factory.FetchReServiceByCode(suite.DB(), models.ReServiceCodeDOSHUT) diff --git a/pkg/handlers/primeapiv3/mto_service_item_test.go b/pkg/handlers/primeapiv3/mto_service_item_test.go index b0b02a71aca..cd7bd7a3fc1 100644 --- a/pkg/handlers/primeapiv3/mto_service_item_test.go +++ b/pkg/handlers/primeapiv3/mto_service_item_test.go @@ -195,6 +195,11 @@ func (suite *HandlerSuite) TestCreateMTOServiceItemHandler() { Model: mto, LinkOnly: true, }, + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeInternational, + }, + }, }, nil) mtoShipment.PrimeEstimatedWeight = nil factory.FetchReServiceByCode(suite.DB(), models.ReServiceCodeDOSHUT) From cf795c0ead353342335dc160358fda1e0a2a8640 Mon Sep 17 00:00:00 2001 From: ryan-mchugh Date: Thu, 20 Feb 2025 18:02:48 +0000 Subject: [PATCH 18/26] B-22039 - remove lat & lon per locations enumerated in description. --- pkg/gen/adminapi/embedded_spec.go | 20 ------------------- .../adminmessages/transportation_office.go | 8 -------- pkg/handlers/adminapi/offices.go | 2 -- playwright/tests/admin/offices.spec.js | 2 +- src/pages/Admin/Offices/OfficeList.jsx | 2 -- swagger-def/admin.yaml | 8 -------- swagger/admin.yaml | 8 -------- 7 files changed, 1 insertion(+), 49 deletions(-) diff --git a/pkg/gen/adminapi/embedded_spec.go b/pkg/gen/adminapi/embedded_spec.go index 3433894752a..27a0e153ff4 100644 --- a/pkg/gen/adminapi/embedded_spec.go +++ b/pkg/gen/adminapi/embedded_spec.go @@ -3296,16 +3296,6 @@ func init() { "format": "uuid", "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" }, - "latitude": { - "type": "number", - "format": "float", - "example": 29.382973 - }, - "longitude": { - "type": "number", - "format": "float", - "example": -98.62759 - }, "name": { "type": "string", "example": "Fort Bragg North Station" @@ -6988,16 +6978,6 @@ func init() { "format": "uuid", "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" }, - "latitude": { - "type": "number", - "format": "float", - "example": 29.382973 - }, - "longitude": { - "type": "number", - "format": "float", - "example": -98.62759 - }, "name": { "type": "string", "example": "Fort Bragg North Station" diff --git a/pkg/gen/adminmessages/transportation_office.go b/pkg/gen/adminmessages/transportation_office.go index f291b011d51..4468ea5b772 100644 --- a/pkg/gen/adminmessages/transportation_office.go +++ b/pkg/gen/adminmessages/transportation_office.go @@ -41,14 +41,6 @@ type TransportationOffice struct { // Format: uuid ID *strfmt.UUID `json:"id"` - // latitude - // Example: 29.382973 - Latitude float32 `json:"latitude,omitempty"` - - // longitude - // Example: -98.62759 - Longitude float32 `json:"longitude,omitempty"` - // name // Example: Fort Bragg North Station // Required: true diff --git a/pkg/handlers/adminapi/offices.go b/pkg/handlers/adminapi/offices.go index 5a189d7348e..c5f2aff5609 100644 --- a/pkg/handlers/adminapi/offices.go +++ b/pkg/handlers/adminapi/offices.go @@ -22,8 +22,6 @@ func payloadForOfficeModel(o models.TransportationOffice) *adminmessages.Transpo Address: payloadForAddressModel(&o.Address), Gbloc: o.Gbloc, PhoneLines: payloadForPhoneLines(o.PhoneLines), - Latitude: o.Latitude, - Longitude: o.Longitude, } } diff --git a/playwright/tests/admin/offices.spec.js b/playwright/tests/admin/offices.spec.js index bf9d2745a8e..ceee23ed984 100644 --- a/playwright/tests/admin/offices.spec.js +++ b/playwright/tests/admin/offices.spec.js @@ -16,7 +16,7 @@ test.describe('Offices Page', () => { await expect(page.locator('header')).toContainText('Offices'); await expect(page.getByLabel('Search by Office Name')).toBeEditable(); - const columnLabels = ['Id', 'Name', 'Latitude', 'Longitude', 'Gbloc']; + const columnLabels = ['Id', 'Name', 'Gbloc']; await adminPage.expectRoleLabelsByText('columnheader', columnLabels); }); }); diff --git a/src/pages/Admin/Offices/OfficeList.jsx b/src/pages/Admin/Offices/OfficeList.jsx index 27a0df55625..50abfc385c4 100644 --- a/src/pages/Admin/Offices/OfficeList.jsx +++ b/src/pages/Admin/Offices/OfficeList.jsx @@ -16,8 +16,6 @@ const OfficeList = () => ( - - diff --git a/swagger-def/admin.yaml b/swagger-def/admin.yaml index ccbce27b6bb..f381b35b799 100644 --- a/swagger-def/admin.yaml +++ b/swagger-def/admin.yaml @@ -1047,14 +1047,6 @@ definitions: type: string pattern: "^[A-Z]{4}$" example: JENQ - latitude: - type: number - format: float - example: 29.382973 - longitude: - type: number - format: float - example: -98.62759 createdAt: type: string format: date-time diff --git a/swagger/admin.yaml b/swagger/admin.yaml index 5082f703079..aec58e45fe5 100644 --- a/swagger/admin.yaml +++ b/swagger/admin.yaml @@ -1054,14 +1054,6 @@ definitions: type: string pattern: ^[A-Z]{4}$ example: JENQ - latitude: - type: number - format: float - example: 29.382973 - longitude: - type: number - format: float - example: -98.62759 createdAt: type: string format: date-time From cfe9b4f426ee9e6131d18049a1f0bf41a188aee1 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Sat, 22 Feb 2025 19:03:06 +0000 Subject: [PATCH 19/26] initial commit, updated shorthaul pricer to consider PPMs and improved same ZIP logic --- pkg/services/ghc_rate_engine.go | 2 +- .../domestic_shorthaul_pricer.go | 15 ++++++-- .../domestic_shorthaul_pricer_test.go | 36 ++++++++++++++++--- pkg/services/ghcrateengine/pricer_helpers.go | 3 +- pkg/services/mocks/DomesticShorthaulPricer.go | 22 ++++++------ .../mto_service_item_creator.go | 2 +- pkg/services/ppmshipment/ppm_estimator.go | 20 +++++++++-- 7 files changed, 75 insertions(+), 25 deletions(-) diff --git a/pkg/services/ghc_rate_engine.go b/pkg/services/ghc_rate_engine.go index 8944ad1b1bc..f10ccdd8bbc 100644 --- a/pkg/services/ghc_rate_engine.go +++ b/pkg/services/ghc_rate_engine.go @@ -57,7 +57,7 @@ type DomesticLinehaulPricer interface { // //go:generate mockery --name DomesticShorthaulPricer type DomesticShorthaulPricer interface { - Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, distance unit.Miles, weight unit.Pound, serviceArea string) (unit.Cents, PricingDisplayParams, error) + Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, distance unit.Miles, weight unit.Pound, serviceArea string, isPPM bool) (unit.Cents, PricingDisplayParams, error) ParamsPricer } diff --git a/pkg/services/ghcrateengine/domestic_shorthaul_pricer.go b/pkg/services/ghcrateengine/domestic_shorthaul_pricer.go index 44aa7d161b6..08600956811 100644 --- a/pkg/services/ghcrateengine/domestic_shorthaul_pricer.go +++ b/pkg/services/ghcrateengine/domestic_shorthaul_pricer.go @@ -28,7 +28,8 @@ func (p domesticShorthaulPricer) Price(appCtx appcontext.AppContext, contractCod referenceDate time.Time, distance unit.Miles, weight unit.Pound, - serviceArea string) (totalCost unit.Cents, params services.PricingDisplayParams, err error) { + serviceArea string, + isPPM bool) (totalCost unit.Cents, params services.PricingDisplayParams, err error) { // Validate parameters if len(contractCode) == 0 { return 0, nil, errors.New("ContractCode is required") @@ -36,7 +37,7 @@ func (p domesticShorthaulPricer) Price(appCtx appcontext.AppContext, contractCod if referenceDate.IsZero() { return 0, nil, errors.New("ReferenceDate is required") } - if weight < minDomesticWeight { + if !isPPM && weight < minDomesticWeight { return 0, nil, fmt.Errorf("Weight must be a minimum of %d", minDomesticWeight) } if distance <= 0 { @@ -110,5 +111,13 @@ func (p domesticShorthaulPricer) PriceUsingParams(appCtx appcontext.AppContext, return unit.Cents(0), nil, err } - return p.Price(appCtx, contractCode, referenceDate, unit.Miles(distanceZip), unit.Pound(weightBilled), serviceAreaOrigin) + var isPPM = false + if params[0].PaymentServiceItem.MTOServiceItem.MTOShipment.ShipmentType == models.MTOShipmentTypePPM { + // PPMs do not require minimums for a shipment's weight or distance + // this flag is passed into the Price function to ensure the weight and distance mins + // are not enforced for PPMs + isPPM = true + } + + return p.Price(appCtx, contractCode, referenceDate, unit.Miles(distanceZip), unit.Pound(weightBilled), serviceAreaOrigin, isPPM) } diff --git a/pkg/services/ghcrateengine/domestic_shorthaul_pricer_test.go b/pkg/services/ghcrateengine/domestic_shorthaul_pricer_test.go index 7f43328515a..d8f157f2682 100644 --- a/pkg/services/ghcrateengine/domestic_shorthaul_pricer_test.go +++ b/pkg/services/ghcrateengine/domestic_shorthaul_pricer_test.go @@ -17,6 +17,8 @@ const ( dshTestMileage = 1200 ) +var dshRequestedPickupDate = time.Date(testdatagen.TestYear, time.June, 5, 7, 33, 11, 456, time.UTC) + func (suite *GHCRateEngineServiceSuite) TestPriceDomesticShorthaulWithServiceItemParamsBadData() { requestedPickup := time.Date(testdatagen.TestYear, peakStart.month, peakStart.day, 0, 0, 0, 0, time.UTC).Format(DateParamFormat) pricer := NewDomesticShorthaulPricer() @@ -120,6 +122,7 @@ func (suite *GHCRateEngineServiceSuite) TestPriceDomesticShorthaulWithServiceIte } func (suite *GHCRateEngineServiceSuite) TestPriceDomesticShorthaul() { + isPPM := false suite.Run("success shorthaul cost within peak period", func() { requestedPickup := time.Date(testdatagen.TestYear, peakStart.month, peakStart.day, 0, 0, 0, 0, time.UTC).Format(DateParamFormat) suite.setUpDomesticShorthaulData() @@ -135,6 +138,7 @@ func (suite *GHCRateEngineServiceSuite) TestPriceDomesticShorthaul() { dshTestMileage, dshTestWeight, dshTestServiceArea, + isPPM, ) expectedCost := unit.Cents(6566400) suite.NoError(err) @@ -158,6 +162,7 @@ func (suite *GHCRateEngineServiceSuite) TestPriceDomesticShorthaul() { dshTestMileage, dshTestWeight, dshTestServiceArea, + isPPM, ) expectedCost := unit.Cents(5702400) suite.NoError(err) @@ -176,6 +181,7 @@ func (suite *GHCRateEngineServiceSuite) TestPriceDomesticShorthaul() { dshTestMileage, dshTestWeight, dshTestServiceArea, + isPPM, ) suite.Error(err) @@ -194,6 +200,7 @@ func (suite *GHCRateEngineServiceSuite) TestPriceDomesticShorthaul() { dshTestMileage, dshTestWeight, dshTestServiceArea, + isPPM, ) suite.Error(err) @@ -212,6 +219,7 @@ func (suite *GHCRateEngineServiceSuite) TestPriceDomesticShorthaul() { dshTestMileage, unit.Pound(499), dshTestServiceArea, + isPPM, ) suite.Equal(unit.Cents(0), cost) suite.Error(err) @@ -219,6 +227,24 @@ func (suite *GHCRateEngineServiceSuite) TestPriceDomesticShorthaul() { suite.Nil(rateEngineParams) }) + suite.Run("successfully finds shorthaul price for ppm with weight < 500 lbs with Price method", func() { + suite.setUpDomesticShorthaulData() + pricer := NewDomesticShorthaulPricer() + isPPM = true + // the PPM price for weights < 500 should be prorated from a base of 500 + basePriceCents, _, err := pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, dshRequestedPickupDate, dshTestMileage, unit.Pound(500), dshTestServiceArea, isPPM) + suite.NoError(err) + + halfPriceCents, _, err := pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, dshRequestedPickupDate, dshTestMileage, unit.Pound(250), dshTestServiceArea, isPPM) + suite.NoError(err) + suite.Equal(basePriceCents/2, halfPriceCents) + + fifthPriceCents, _, err := pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, dshRequestedPickupDate, dshTestMileage, unit.Pound(100), dshTestServiceArea, isPPM) + suite.NoError(err) + suite.Equal(basePriceCents/5, fifthPriceCents) + isPPM = false + }) + suite.Run("validation errors", func() { suite.setUpDomesticShorthaulData() pricer := NewDomesticShorthaulPricer() @@ -226,31 +252,31 @@ func (suite *GHCRateEngineServiceSuite) TestPriceDomesticShorthaul() { requestedPickupDate := time.Date(testdatagen.TestYear, time.July, 4, 0, 0, 0, 0, time.UTC) // No contract code - _, rateEngineParams, err := pricer.Price(suite.AppContextForTest(), "", requestedPickupDate, dshTestMileage, dshTestWeight, dshTestServiceArea) + _, rateEngineParams, err := pricer.Price(suite.AppContextForTest(), "", requestedPickupDate, dshTestMileage, dshTestWeight, dshTestServiceArea, isPPM) suite.Error(err) suite.Equal("ContractCode is required", err.Error()) suite.Nil(rateEngineParams) // No reference date - _, rateEngineParams, err = pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, time.Time{}, dshTestMileage, dshTestWeight, dshTestServiceArea) + _, rateEngineParams, err = pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, time.Time{}, dshTestMileage, dshTestWeight, dshTestServiceArea, isPPM) suite.Error(err) suite.Equal("ReferenceDate is required", err.Error()) suite.Nil(rateEngineParams) // No distance - _, rateEngineParams, err = pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, requestedPickupDate, 0, dshTestWeight, dshTestServiceArea) + _, rateEngineParams, err = pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, requestedPickupDate, 0, dshTestWeight, dshTestServiceArea, isPPM) suite.Error(err) suite.Equal("Distance must be greater than 0", err.Error()) suite.Nil(rateEngineParams) // No weight - _, rateEngineParams, err = pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, requestedPickupDate, dshTestMileage, 0, dshTestServiceArea) + _, rateEngineParams, err = pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, requestedPickupDate, dshTestMileage, 0, dshTestServiceArea, isPPM) suite.Error(err) suite.Equal("Weight must be a minimum of 500", err.Error()) suite.Nil(rateEngineParams) // No service area - _, rateEngineParams, err = pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, requestedPickupDate, dshTestMileage, dshTestWeight, "") + _, rateEngineParams, err = pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, requestedPickupDate, dshTestMileage, dshTestWeight, "", isPPM) suite.Error(err) suite.Equal("ServiceArea is required", err.Error()) suite.Nil(rateEngineParams) diff --git a/pkg/services/ghcrateengine/pricer_helpers.go b/pkg/services/ghcrateengine/pricer_helpers.go index cb804b0da49..9f88767d652 100644 --- a/pkg/services/ghcrateengine/pricer_helpers.go +++ b/pkg/services/ghcrateengine/pricer_helpers.go @@ -267,7 +267,8 @@ func priceDomesticPickupDeliverySIT(appCtx appcontext.AppContext, pickupDelivery if zip3Original == zip3Actual { // Do a normal shorthaul calculation shorthaulPricer := NewDomesticShorthaulPricer() - totalPriceCents, displayParams, err := shorthaulPricer.Price(appCtx, contractCode, referenceDate, distance, weight, serviceArea) + isPPM := false + totalPriceCents, displayParams, err := shorthaulPricer.Price(appCtx, contractCode, referenceDate, distance, weight, serviceArea, isPPM) if err != nil { return unit.Cents(0), nil, fmt.Errorf("could not price shorthaul: %w", err) } diff --git a/pkg/services/mocks/DomesticShorthaulPricer.go b/pkg/services/mocks/DomesticShorthaulPricer.go index 029872ef7e4..5ffc3ad9536 100644 --- a/pkg/services/mocks/DomesticShorthaulPricer.go +++ b/pkg/services/mocks/DomesticShorthaulPricer.go @@ -20,9 +20,9 @@ type DomesticShorthaulPricer struct { mock.Mock } -// Price provides a mock function with given fields: appCtx, contractCode, requestedPickupDate, distance, weight, serviceArea -func (_m *DomesticShorthaulPricer) Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, distance unit.Miles, weight unit.Pound, serviceArea string) (unit.Cents, services.PricingDisplayParams, error) { - ret := _m.Called(appCtx, contractCode, requestedPickupDate, distance, weight, serviceArea) +// Price provides a mock function with given fields: appCtx, contractCode, requestedPickupDate, distance, weight, serviceArea, isPPM +func (_m *DomesticShorthaulPricer) Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, distance unit.Miles, weight unit.Pound, serviceArea string, isPPM bool) (unit.Cents, services.PricingDisplayParams, error) { + ret := _m.Called(appCtx, contractCode, requestedPickupDate, distance, weight, serviceArea, isPPM) if len(ret) == 0 { panic("no return value specified for Price") @@ -31,25 +31,25 @@ func (_m *DomesticShorthaulPricer) Price(appCtx appcontext.AppContext, contractC var r0 unit.Cents var r1 services.PricingDisplayParams var r2 error - if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, time.Time, unit.Miles, unit.Pound, string) (unit.Cents, services.PricingDisplayParams, error)); ok { - return rf(appCtx, contractCode, requestedPickupDate, distance, weight, serviceArea) + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, time.Time, unit.Miles, unit.Pound, string, bool) (unit.Cents, services.PricingDisplayParams, error)); ok { + return rf(appCtx, contractCode, requestedPickupDate, distance, weight, serviceArea, isPPM) } - if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, time.Time, unit.Miles, unit.Pound, string) unit.Cents); ok { - r0 = rf(appCtx, contractCode, requestedPickupDate, distance, weight, serviceArea) + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, time.Time, unit.Miles, unit.Pound, string, bool) unit.Cents); ok { + r0 = rf(appCtx, contractCode, requestedPickupDate, distance, weight, serviceArea, isPPM) } else { r0 = ret.Get(0).(unit.Cents) } - if rf, ok := ret.Get(1).(func(appcontext.AppContext, string, time.Time, unit.Miles, unit.Pound, string) services.PricingDisplayParams); ok { - r1 = rf(appCtx, contractCode, requestedPickupDate, distance, weight, serviceArea) + if rf, ok := ret.Get(1).(func(appcontext.AppContext, string, time.Time, unit.Miles, unit.Pound, string, bool) services.PricingDisplayParams); ok { + r1 = rf(appCtx, contractCode, requestedPickupDate, distance, weight, serviceArea, isPPM) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(services.PricingDisplayParams) } } - if rf, ok := ret.Get(2).(func(appcontext.AppContext, string, time.Time, unit.Miles, unit.Pound, string) error); ok { - r2 = rf(appCtx, contractCode, requestedPickupDate, distance, weight, serviceArea) + if rf, ok := ret.Get(2).(func(appcontext.AppContext, string, time.Time, unit.Miles, unit.Pound, string, bool) error); ok { + r2 = rf(appCtx, contractCode, requestedPickupDate, distance, weight, serviceArea, isPPM) } else { r2 = ret.Error(2) } diff --git a/pkg/services/mto_service_item/mto_service_item_creator.go b/pkg/services/mto_service_item/mto_service_item_creator.go index 3b46e33ea3a..aaa7b4c3a36 100644 --- a/pkg/services/mto_service_item/mto_service_item_creator.go +++ b/pkg/services/mto_service_item/mto_service_item_creator.go @@ -151,7 +151,7 @@ func (o *mtoServiceItemCreator) FindEstimatedPrice(appCtx appcontext.AppContext, return 0, err } } - price, _, err = o.shorthaulPricer.Price(appCtx, contractCode, requestedPickupDate, unit.Miles(distance), *adjustedWeight, domesticServiceArea.ServiceArea) + price, _, err = o.shorthaulPricer.Price(appCtx, contractCode, requestedPickupDate, unit.Miles(distance), *adjustedWeight, domesticServiceArea.ServiceArea, isPPM) if err != nil { return 0, err } diff --git a/pkg/services/ppmshipment/ppm_estimator.go b/pkg/services/ppmshipment/ppm_estimator.go index cf77b16fb04..d162aa3a722 100644 --- a/pkg/services/ppmshipment/ppm_estimator.go +++ b/pkg/services/ppmshipment/ppm_estimator.go @@ -489,8 +489,16 @@ func (f estimatePPM) calculatePrice(appCtx appcontext.AppContext, ppmShipment *m } } - if pickupPostal[0:3] == destPostal[0:3] { - serviceItemsToPrice[0] = models.MTOServiceItem{ReService: models.ReService{Code: models.ReServiceCodeDSH}, MTOShipmentID: &ppmShipment.ShipmentID} + // if the ZIPs are the same, we need to replace the DLH service item with DSH + if len(pickupPostal) >= 3 && len(destPostal) >= 3 && pickupPostal[:3] == destPostal[:3] { + if pickupPostal[0:3] == destPostal[0:3] { + for i, serviceItem := range serviceItemsToPrice { + if serviceItem.ReService.Code == models.ReServiceCodeDLH { + serviceItemsToPrice[i] = models.MTOServiceItem{ReService: models.ReService{Code: models.ReServiceCodeDSH}, MTOShipmentID: &ppmShipment.ShipmentID} + break + } + } + } } // Get a list of all the pricing params needed to calculate the price for each service item @@ -630,9 +638,15 @@ func (f estimatePPM) priceBreakdown(appCtx appcontext.AppContext, ppmShipment *m destPostal = ppmShipment.DestinationAddress.PostalCode } + // if the ZIPs are the same, we need to replace the DLH service item with DSH if len(pickupPostal) >= 3 && len(destPostal) >= 3 && pickupPostal[:3] == destPostal[:3] { if pickupPostal[0:3] == destPostal[0:3] { - serviceItemsToPrice[0] = models.MTOServiceItem{ReService: models.ReService{Code: models.ReServiceCodeDSH}, MTOShipmentID: &ppmShipment.ShipmentID} + for i, serviceItem := range serviceItemsToPrice { + if serviceItem.ReService.Code == models.ReServiceCodeDLH { + serviceItemsToPrice[i] = models.MTOServiceItem{ReService: models.ReService{Code: models.ReServiceCodeDSH}, MTOShipmentID: &ppmShipment.ShipmentID} + break + } + } } } From 44abb7b33a8b08c81f1e0152650fbf52c4a40e9b Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Mon, 24 Feb 2025 17:45:14 +0000 Subject: [PATCH 20/26] initial commit, all functionalty and tests added --- migrations/app/ddl_functions_manifest.txt | 1 + ...223023132_fn_get_counseling_offices.up.sql | 171 ++++++++++++++ pkg/models/transportation_office.go | 14 ++ pkg/models/transportation_office_test.go | 95 ++++++++ .../transportation_office_fetcher.go | 176 +------------- .../transportation_office_fetcher_test.go | 223 +----------------- .../EditOrdersForm/EditOrdersForm.jsx | 64 +++-- .../EditOrdersForm/EditOrdersForm.stories.jsx | 81 ++++--- .../EditOrdersForm/EditOrdersForm.test.jsx | 215 ++++++++++------- .../OrdersInfoForm/OrdersInfoForm.jsx | 54 ++--- .../Office/AddOrdersForm/AddOrdersForm.jsx | 11 +- src/pages/MyMove/AddOrders.test.jsx | 24 +- src/pages/MyMove/EditOrders.test.jsx | 28 +++ 13 files changed, 594 insertions(+), 563 deletions(-) create mode 100644 migrations/app/ddl_migrations/ddl_functions/20250223023132_fn_get_counseling_offices.up.sql diff --git a/migrations/app/ddl_functions_manifest.txt b/migrations/app/ddl_functions_manifest.txt index 237796e829e..e034b0a7981 100644 --- a/migrations/app/ddl_functions_manifest.txt +++ b/migrations/app/ddl_functions_manifest.txt @@ -1,3 +1,4 @@ # This is the functions(procedures) migrations manifest. # If a migration is not recorded here, then it will error. # Naming convention: fn_some_function.up.sql running will create this file. +20250223023132_fn_get_counseling_offices.up.sql diff --git a/migrations/app/ddl_migrations/ddl_functions/20250223023132_fn_get_counseling_offices.up.sql b/migrations/app/ddl_migrations/ddl_functions/20250223023132_fn_get_counseling_offices.up.sql new file mode 100644 index 00000000000..50dee4dd9e2 --- /dev/null +++ b/migrations/app/ddl_migrations/ddl_functions/20250223023132_fn_get_counseling_offices.up.sql @@ -0,0 +1,171 @@ +--B-22660 Daniel Jordan added get_duty_location_info +CREATE OR REPLACE FUNCTION get_duty_location_info(p_duty_location_id UUID) +RETURNS TABLE (duty_addr_id UUID, is_oconus BOOLEAN) +LANGUAGE plpgsql AS $$ +BEGIN + RETURN QUERY + SELECT dl.address_id, a.is_oconus + FROM duty_locations dl + JOIN addresses a ON a.id = dl.address_id + WHERE dl.id = p_duty_location_id; +END; +$$; + +--B-22660 Daniel Jordan added get_service_affiliation +CREATE OR REPLACE FUNCTION get_service_affiliation(p_service_member_id UUID) +RETURNS TEXT +LANGUAGE plpgsql AS $$ +DECLARE + service_affiliation TEXT; +BEGIN + SELECT affiliation INTO service_affiliation + FROM service_members + WHERE id = p_service_member_id; + + RETURN service_affiliation; +END; +$$; + +--B-22660 Daniel Jordan added get_department_indicator +CREATE OR REPLACE FUNCTION get_department_indicator(p_service_affiliation TEXT) +RETURNS TEXT +LANGUAGE plpgsql AS $$ +DECLARE + dept_indicator TEXT; +BEGIN + IF p_service_affiliation IN ('AIR_FORCE', 'SPACE_FORCE') THEN + dept_indicator := 'AIR_AND_SPACE_FORCE'; + ELSIF p_service_affiliation IN ('NAVY', 'MARINES') THEN + dept_indicator := 'NAVY_AND_MARINES'; + ELSIF p_service_affiliation = 'ARMY' THEN + dept_indicator := 'ARMY'; + ELSIF p_service_affiliation = 'COAST_GUARD' THEN + dept_indicator := 'COAST_GUARD'; + ELSE + RAISE EXCEPTION 'Invalid affiliation: %', p_service_affiliation; + END IF; + + RETURN dept_indicator; +END; +$$; + +--B-22660 Daniel Jordan added get_gbloc_indicator +CREATE OR REPLACE FUNCTION get_gbloc_indicator(p_duty_addr_id UUID, p_dept_indicator TEXT) +RETURNS TEXT +LANGUAGE plpgsql AS $$ +DECLARE + gbloc_indicator TEXT; +BEGIN + SELECT j.code INTO gbloc_indicator + FROM addresses a + JOIN v_locations v ON a.us_post_region_cities_id = v.uprc_id + JOIN re_oconus_rate_areas o ON v.uprc_id = o.us_post_region_cities_id + JOIN re_rate_areas r ON o.rate_area_id = r.id + JOIN gbloc_aors g ON o.id = g.oconus_rate_area_id + JOIN jppso_regions j ON g.jppso_regions_id = j.id + WHERE a.id = p_duty_addr_id + AND (g.department_indicator = p_dept_indicator OR g.department_indicator IS NULL) + LIMIT 1; + + IF gbloc_indicator IS NULL THEN + RAISE EXCEPTION 'Cannot determine GBLOC for duty location'; + END IF; + + RETURN gbloc_indicator; +END; +$$; + +--B-22660 Daniel Jordan added fetch_counseling_offices_for_oconus +CREATE OR REPLACE FUNCTION fetch_counseling_offices_for_oconus(p_duty_location_id UUID, p_gbloc_indicator TEXT) +RETURNS TABLE (id UUID, name TEXT) +LANGUAGE plpgsql AS $$ +BEGIN + RETURN QUERY + SELECT toff.id, toff.name + FROM duty_locations dl + JOIN addresses a ON dl.address_id = a.id + JOIN v_locations v ON (a.us_post_region_cities_id = v.uprc_id OR v.uprc_id IS NULL) + AND a.country_id = v.country_id + JOIN re_oconus_rate_areas r ON r.us_post_region_cities_id = v.uprc_id + JOIN gbloc_aors ga ON ga.oconus_rate_area_id = r.id + JOIN jppso_regions j ON ga.jppso_regions_id = j.id + JOIN transportation_offices toff ON j.code = toff.gbloc + JOIN addresses toff_addr ON toff.address_id = toff_addr.id + LEFT JOIN zip3_distances zd + ON ( + (substring(a.postal_code, 1, 3) = zd.from_zip3 AND substring(toff_addr.postal_code, 1, 3) = zd.to_zip3) + OR + (substring(a.postal_code, 1, 3) = zd.to_zip3 AND substring(toff_addr.postal_code, 1, 3) = zd.from_zip3) + ) + WHERE dl.provides_services_counseling = true + AND dl.id = p_duty_location_id + AND j.code = p_gbloc_indicator + AND toff.provides_ppm_closeout = true + ORDER BY COALESCE(zd.distance_miles, 0) ASC; +END; +$$; + +--B-22660 Daniel Jordan added fetch_counseling_offices_for_conus +CREATE OR REPLACE FUNCTION fetch_counseling_offices_for_conus(p_duty_location_id UUID) +RETURNS TABLE (id UUID, name TEXT) +LANGUAGE plpgsql AS $$ +BEGIN + RETURN QUERY + SELECT + toff.id, + toff.name + FROM postal_code_to_gblocs pcg + JOIN addresses a ON pcg.postal_code = a.postal_code + JOIN duty_locations dl ON a.id = dl.address_id + JOIN transportation_offices toff ON pcg.gbloc = toff.gbloc + JOIN addresses toff_addr ON toff.address_id = toff_addr.id + JOIN duty_locations d2 ON toff.id = d2.transportation_office_id + JOIN re_us_post_regions rup ON toff_addr.postal_code = rup.uspr_zip_id + LEFT JOIN zip3_distances zd + ON ( + (substring(a.postal_code, 1, 3) = zd.from_zip3 AND substring(toff_addr.postal_code, 1, 3) = zd.to_zip3) + OR + (substring(a.postal_code, 1, 3) = zd.to_zip3 AND substring(toff_addr.postal_code, 1, 3) = zd.from_zip3) + ) + WHERE dl.provides_services_counseling = true + AND dl.id = p_duty_location_id + AND d2.provides_services_counseling = true + GROUP BY toff.id, toff.name, zd.distance_miles + ORDER BY COALESCE(zd.distance_miles, 0), toff.name ASC; +END; +$$; + +--B-22660 Daniel Jordan added get_counseling_offices +CREATE OR REPLACE FUNCTION get_counseling_offices( + p_duty_location_id UUID, + p_service_member_id UUID +) +RETURNS TABLE (id UUID, name TEXT) +LANGUAGE plpgsql AS $$ +DECLARE + is_address_oconus BOOLEAN; + duty_address_id UUID; + service_affiliation TEXT; + dept_indicator TEXT; + gbloc_indicator TEXT; +BEGIN + + SELECT duty_addr_id, is_oconus INTO duty_address_id, is_address_oconus + FROM get_duty_location_info(p_duty_location_id); + + IF duty_address_id IS NULL THEN + RAISE EXCEPTION 'Duty location % not found when searching for counseling offices', p_duty_location_id; + END IF; + + IF is_address_oconus THEN + service_affiliation := get_service_affiliation(p_service_member_id); + dept_indicator := get_department_indicator(service_affiliation); + + gbloc_indicator := get_gbloc_indicator(duty_address_id, dept_indicator); + + RETURN QUERY SELECT * FROM fetch_counseling_offices_for_oconus(p_duty_location_id, gbloc_indicator); + ELSE + RETURN QUERY SELECT * FROM fetch_counseling_offices_for_conus(p_duty_location_id); + END IF; +END; +$$; diff --git a/pkg/models/transportation_office.go b/pkg/models/transportation_office.go index fe5ba6261ea..21a53cc621f 100644 --- a/pkg/models/transportation_office.go +++ b/pkg/models/transportation_office.go @@ -70,3 +70,17 @@ func FetchNearestTransportationOffice(tx *pop.Connection, long float32, lat floa return to, nil } + +// GetCounselingOffices is a db function that returns all the transportation offices in the GBLOC +// of the given duty location where provides_services_counseling = true +func GetCounselingOffices(db *pop.Connection, dutyLocationID uuid.UUID, serviceMemberID uuid.UUID) (TransportationOffices, error) { + var officeList TransportationOffices + + err := db.RawQuery("SELECT * FROM get_counseling_offices($1, $2)", dutyLocationID, serviceMemberID). + All(&officeList) + if err != nil { + return officeList, err + } + + return officeList, nil +} diff --git a/pkg/models/transportation_office_test.go b/pkg/models/transportation_office_test.go index b553d410b3e..dc16211d3d8 100644 --- a/pkg/models/transportation_office_test.go +++ b/pkg/models/transportation_office_test.go @@ -10,6 +10,9 @@ package models_test import ( + "github.com/gofrs/uuid" + + "github.com/transcom/mymove/pkg/factory" m "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services/address" ) @@ -78,3 +81,95 @@ func (suite *ModelSuite) Test_TransportationOffice() { suite.Equal(ppo.ID, loadedOffice.ID) suite.Equal(jppso.ID, loadedOffice.ShippingOffice.ID) } + +func (suite *ModelSuite) TestGetCounselingOffices() { + customAddress1 := m.Address{ + ID: uuid.Must(uuid.NewV4()), + PostalCode: "59801", + } + factory.BuildDutyLocation(suite.DB(), []factory.Customization{ + {Model: customAddress1, Type: &factory.Addresses.DutyLocationAddress}, + { + Model: m.DutyLocation{ + ProvidesServicesCounseling: false, + }, + }, + { + Model: m.TransportationOffice{ + Name: "PPPO Holloman AFB - USAF", + }, + }, + }, nil) + + // duty locations in KKFA with provides_services_counseling = true + customAddress2 := m.Address{ + ID: uuid.Must(uuid.NewV4()), + PostalCode: "59801", + } + factory.BuildDutyLocation(suite.DB(), []factory.Customization{ + {Model: customAddress2, Type: &factory.Addresses.DutyLocationAddress}, + { + Model: m.DutyLocation{ + ProvidesServicesCounseling: true, + }, + }, + { + Model: m.TransportationOffice{ + Name: "PPPO Hill AFB - USAF", + }, + }, + }, nil) + + customAddress3 := m.Address{ + ID: uuid.Must(uuid.NewV4()), + PostalCode: "59801", + } + origDutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ + {Model: customAddress3, Type: &factory.Addresses.DutyLocationAddress}, + { + Model: m.DutyLocation{ + ProvidesServicesCounseling: true, + }, + }, + { + Model: m.TransportationOffice{ + Name: "PPPO Travis AFB - USAF", + Gbloc: "KKFA", + ProvidesCloseout: true, + }, + }, + }, nil) + + // this one will not show in the return since it is not KKFA + customAddress4 := m.Address{ + ID: uuid.Must(uuid.NewV4()), + PostalCode: "20906", + } + factory.BuildDutyLocation(suite.DB(), []factory.Customization{ + {Model: customAddress4, Type: &factory.Addresses.DutyLocationAddress}, + { + Model: m.DutyLocation{ + ProvidesServicesCounseling: true, + }, + }, + { + Model: m.TransportationOffice{ + Name: "PPPO Fort Meade - USA", + Gbloc: "BGCA", + ProvidesCloseout: true, + }, + }, + }, nil) + + armyAffliation := m.AffiliationARMY + serviceMember := factory.BuildServiceMember(suite.DB(), []factory.Customization{ + { + Model: m.ServiceMember{ + Affiliation: &armyAffliation, + }, + }, + }, nil) + offices, err := m.GetCounselingOffices(suite.DB(), origDutyLocation.ID, serviceMember.ID) + suite.NoError(err) + suite.Len(offices, 2) +} diff --git a/pkg/services/transportation_office/transportation_office_fetcher.go b/pkg/services/transportation_office/transportation_office_fetcher.go index 8fde2e98093..e178ef1adc5 100644 --- a/pkg/services/transportation_office/transportation_office_fetcher.go +++ b/pkg/services/transportation_office/transportation_office_fetcher.go @@ -2,7 +2,6 @@ package transportationoffice import ( "database/sql" - "fmt" "github.com/gofrs/uuid" "github.com/pkg/errors" @@ -13,12 +12,6 @@ import ( "github.com/transcom/mymove/pkg/services" ) -type oconusGblocDepartmentIndicator struct { - Gbloc string `db:"gbloc" rw:"r"` - RateAreaName string `db:"rate_area_name" rw:"r"` - DepartmentIndicator *string `db:"department_indicator" rw:"r"` -} - type transportationOfficesFetcher struct { } @@ -133,8 +126,10 @@ func ListDistinctGBLOCs(appCtx appcontext.AppContext) (models.GBLOCs, error) { return gblocList, err } +// return all the transportation offices in the GBLOC of the given duty location where provides_services_counseling = true +// serviceMemberID is only provided when this function is called by the office handler func (o transportationOfficesFetcher) GetCounselingOffices(appCtx appcontext.AppContext, dutyLocationID uuid.UUID, serviceMemberID uuid.UUID) (*models.TransportationOffices, error) { - officeList, err := findCounselingOffice(appCtx, dutyLocationID, serviceMemberID) + officeList, err := models.GetCounselingOffices(appCtx.DB(), dutyLocationID, serviceMemberID) if err != nil { switch err { @@ -147,168 +142,3 @@ func (o transportationOfficesFetcher) GetCounselingOffices(appCtx appcontext.App return &officeList, nil } - -// return all the transportation offices in the GBLOC of the given duty location where provides_services_counseling = true -// serviceMemberID is only provided when this function is called by the office handler -func findCounselingOffice(appCtx appcontext.AppContext, dutyLocationID uuid.UUID, serviceMemberID uuid.UUID) (models.TransportationOffices, error) { - var officeList []models.TransportationOffice - - duty_location, err := models.FetchDutyLocation(appCtx.DB(), dutyLocationID) - if err != nil { - return officeList, err - } - - var sqlQuery string - - // ******************************** - // Find for oconus duty location - // ******************************** - if *duty_location.Address.IsOconus { - gblocDepartmentIndicator, err := findOconusGblocDepartmentIndicator(appCtx, duty_location, serviceMemberID) - if err != nil { - return officeList, err - } - - sqlQuery = ` - with counseling_offices as ( - SELECT transportation_offices.id, transportation_offices.name, transportation_offices.address_id as counseling_address, - substring(a.postal_code, 1,3 ) as origin_zip, substring(a2.postal_code, 1,3 ) as dest_zip - FROM duty_locations - JOIN addresses a on duty_locations.address_id = a.id - JOIN v_locations v on (a.us_post_region_cities_id = v.uprc_id or v.uprc_id is null) - and a.country_id = v.country_id - JOIN re_oconus_rate_areas r on r.us_post_region_cities_id = v.uprc_id - JOIN gbloc_aors on gbloc_aors.oconus_rate_area_id = r.id - JOIN jppso_regions j on gbloc_aors.jppso_regions_id = j.id - JOIN transportation_offices on j.code = transportation_offices.gbloc - join addresses a2 on a2.id = transportation_offices.address_id - WHERE duty_locations.provides_services_counseling = true and duty_locations.id = $1 and j.code = $2 - and transportation_offices.provides_ppm_closeout = true - ) - SELECT counseling_offices.id, counseling_offices.name - FROM counseling_offices - JOIN addresses cnsl_address on counseling_offices.counseling_address = cnsl_address.id - LEFT JOIN zip3_distances ON ( - (substring(cnsl_address.postal_code,1 ,3) = zip3_distances.to_zip3 - AND counseling_offices.origin_zip = zip3_distances.from_zip3) - OR - (substring(cnsl_address.postal_code,1 ,3) = zip3_distances.from_zip3 - AND counseling_offices.origin_zip = zip3_distances.to_zip3) - ) - group by counseling_offices.id, counseling_offices.name, zip3_distances.distance_miles - ORDER BY coalesce(zip3_distances.distance_miles,0) asc` - - query := appCtx.DB().Q().RawQuery(sqlQuery, dutyLocationID, gblocDepartmentIndicator.Gbloc) - if err := query.All(&officeList); err != nil { - if errors.Cause(err).Error() != models.RecordNotFoundErrorString { - return officeList, err - } - } - return officeList, nil - } - - // ******************************** - // Find for conus duty location - // ******************************** - sqlQuery = ` - with counseling_offices as ( - SELECT transportation_offices.id, transportation_offices.name, transportation_offices.address_id as counseling_address, substring(addresses.postal_code, 1,3 ) as pickup_zip - FROM postal_code_to_gblocs - JOIN addresses on postal_code_to_gblocs.postal_code = addresses.postal_code - JOIN duty_locations on addresses.id = duty_locations.address_id - JOIN transportation_offices on postal_code_to_gblocs.gbloc = transportation_offices.gbloc - WHERE duty_locations.provides_services_counseling = true and duty_locations.id = $1 - ) - SELECT counseling_offices.id, counseling_offices.name - FROM counseling_offices - JOIN duty_locations duty_locations2 on counseling_offices.id = duty_locations2.transportation_office_id - JOIN addresses on counseling_offices.counseling_address = addresses.id - JOIN re_us_post_regions on addresses.postal_code = re_us_post_regions.uspr_zip_id - LEFT JOIN zip3_distances ON ( - (re_us_post_regions.zip3 = zip3_distances.to_zip3 - AND counseling_offices.pickup_zip = zip3_distances.from_zip3) - OR - (re_us_post_regions.zip3 = zip3_distances.from_zip3 - AND counseling_offices.pickup_zip = zip3_distances.to_zip3) - ) - WHERE duty_locations2.provides_services_counseling = true - group by counseling_offices.id, counseling_offices.name, zip3_distances.distance_miles - ORDER BY coalesce(zip3_distances.distance_miles,0), counseling_offices.name asc` - - query := appCtx.DB().Q().RawQuery(sqlQuery, dutyLocationID) - if err := query.All(&officeList); err != nil { - if errors.Cause(err).Error() != models.RecordNotFoundErrorString { - return officeList, err - } - } - - return officeList, nil -} - -func findOconusGblocDepartmentIndicator(appCtx appcontext.AppContext, dutyLocation models.DutyLocation, serviceMemberID uuid.UUID) (*oconusGblocDepartmentIndicator, error) { - serviceMember, err := models.FetchServiceMember(appCtx.DB(), serviceMemberID) - if err != nil { - return nil, err - } - - var oconusGblocDepartmentIndicator []oconusGblocDepartmentIndicator - - sqlQuery := ` - select j.code gbloc, r.name rate_area_name, g.department_indicator - from addresses a, - v_locations v, - re_oconus_rate_areas o, - re_rate_areas r, - jppso_regions j, - gbloc_aors g - where a.id = $1 - and a.us_post_region_cities_id = v.uprc_id - and v.uprc_id = o.us_post_region_cities_id - and o.rate_area_id = r.id - and o.id = g.oconus_rate_area_id - and j.id = g.jppso_regions_id` - - query := appCtx.DB().Q().RawQuery(sqlQuery, dutyLocation.Address.ID) - err = query.All(&oconusGblocDepartmentIndicator) - if err != nil { - return nil, err - } - - // Determine departmentIndicator based on service member's affiliation - var departmentIndicator *string = nil - if serviceMember.Affiliation != nil && (*serviceMember.Affiliation == models.AffiliationAIRFORCE || *serviceMember.Affiliation == models.AffiliationSPACEFORCE) { - departmentIndicator = models.StringPointer(models.DepartmentIndicatorAIRANDSPACEFORCE.String()) - } else if serviceMember.Affiliation != nil && (*serviceMember.Affiliation == models.AffiliationNAVY || *serviceMember.Affiliation == models.AffiliationMARINES) { - departmentIndicator = models.StringPointer(models.DepartmentIndicatorNAVYANDMARINES.String()) - } else if serviceMember.Affiliation != nil && (*serviceMember.Affiliation == models.AffiliationARMY) { - departmentIndicator = models.StringPointer(models.DepartmentIndicatorARMY.String()) - } else if serviceMember.Affiliation != nil && (*serviceMember.Affiliation == models.AffiliationCOASTGUARD) { - departmentIndicator = models.StringPointer(models.DepartmentIndicatorCOASTGUARD.String()) - } - - // Is there a matching GBLOC for duty location address specifically for user's affiliation and duty location Zone(I-V)? - // sample oconusGblocAffiliationInfo[]: - // Gbloc RateAreaName DepartmentIndicator - // JEAT Alaska (Zone) II NULL (default) - // MBFL Alaska (Zone) II AIR_AND_SPACE_FORCE - for _, info := range oconusGblocDepartmentIndicator { - if (info.DepartmentIndicator != nil && departmentIndicator != nil) && (*info.DepartmentIndicator == *departmentIndicator) { - appCtx.Logger().Debug(fmt.Sprintf("Found specific department match -- serviceMember.Affiliation: %s, DutyLocaton: %s, GBLOC: %s, departmentIndicator: %s, RateAreaName: %s, dutyLocation.Address.ID: %s", - serviceMember.Affiliation, dutyLocation.Name, info.Gbloc, *departmentIndicator, info.RateAreaName, dutyLocation.Address.ID)) - return &info, nil - } - } - - // These were no departmentIndicator specific GBLOC for duty location -- return default GBLOC - for _, info := range oconusGblocDepartmentIndicator { - if info.DepartmentIndicator == nil { - appCtx.Logger().Debug(fmt.Sprintf("Did not find specific department match, return default -- serviceMember.Affiliation: %s, DutyLocaton: %s, GBLOC: %s, departmentIndicator: %s, RateAreaName: %s, dutyLocation.Address.ID: %s", - serviceMember.Affiliation, dutyLocation.Name, info.Gbloc, "NIL/NULL", info.RateAreaName, dutyLocation.Address.ID)) - return &info, nil - } - } - - // There is no default and department specific oconusGblocDepartmentIndicator. There is nothing in system to support. This should never happen. - return nil, apperror.NewImplementationError(fmt.Sprintf("Error: Cannot determine GBLOC -- serviceMember.Affiliation: %s, DutyLocaton: %s, departmentIndicator: %s, dutyLocation.Address.ID: %s", - serviceMember.Affiliation, dutyLocation.Name, *departmentIndicator, dutyLocation.Address.ID)) -} diff --git a/pkg/services/transportation_office/transportation_office_fetcher_test.go b/pkg/services/transportation_office/transportation_office_fetcher_test.go index 64c377deb30..801ad821e96 100644 --- a/pkg/services/transportation_office/transportation_office_fetcher_test.go +++ b/pkg/services/transportation_office/transportation_office_fetcher_test.go @@ -1,17 +1,14 @@ package transportationoffice import ( - "fmt" "testing" "github.com/gofrs/uuid" "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/auth" "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" - "github.com/transcom/mymove/pkg/testdatagen" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -100,7 +97,8 @@ func (suite *TransportationOfficeServiceSuite) Test_SortedTransportationOffices( } func (suite *TransportationOfficeServiceSuite) Test_FindCounselingOffices() { - // duty location in KKFA with provies services counseling false + suite.toFetcher = NewTransportationOfficesFetcher() + // duty location in KKFA with provides_services_counseling = false customAddress1 := models.Address{ ID: uuid.Must(uuid.NewV4()), PostalCode: "59801", @@ -119,7 +117,7 @@ func (suite *TransportationOfficeServiceSuite) Test_FindCounselingOffices() { }, }, nil) - // duty location in KKFA with provides services counseling true + // duty locations in KKFA with provides_services_counseling = true customAddress2 := models.Address{ ID: uuid.Must(uuid.NewV4()), PostalCode: "59801", @@ -138,7 +136,6 @@ func (suite *TransportationOfficeServiceSuite) Test_FindCounselingOffices() { }, }, nil) - // duty location in KKFA with provides services counseling true customAddress3 := models.Address{ ID: uuid.Must(uuid.NewV4()), PostalCode: "59801", @@ -159,7 +156,7 @@ func (suite *TransportationOfficeServiceSuite) Test_FindCounselingOffices() { }, }, nil) - // duty location NOT in KKFA with provides services counseling true + // this one will not show in the return since it is not KKFA customAddress4 := models.Address{ ID: uuid.Must(uuid.NewV4()), PostalCode: "20906", @@ -189,217 +186,9 @@ func (suite *TransportationOfficeServiceSuite) Test_FindCounselingOffices() { }, }, nil) - offices, err := findCounselingOffice(suite.AppContextForTest(), origDutyLocation.ID, serviceMember.ID) - + offices, err := suite.toFetcher.GetCounselingOffices(suite.AppContextForTest(), origDutyLocation.ID, serviceMember.ID) suite.NoError(err) - suite.Len(offices, 2) - suite.Equal(offices[0].Name, "PPPO Hill AFB - USAF") - suite.Equal(offices[1].Name, "PPPO Travis AFB - USAF") -} - -func (suite *TransportationOfficeServiceSuite) Test_Oconus_AK_FindCounselingOffices() { - setupServiceMember := func(serviceMemberAffiliation models.ServiceMemberAffiliation) models.ServiceMember { - customServiceMember := models.ServiceMember{ - FirstName: models.StringPointer("Gregory"), - LastName: models.StringPointer("Van der Heide"), - Telephone: models.StringPointer("999-999-9999"), - SecondaryTelephone: models.StringPointer("123-555-9999"), - PersonalEmail: models.StringPointer("peyton@example.com"), - Edipi: models.StringPointer("1000011111"), - Affiliation: &serviceMemberAffiliation, - Suffix: models.StringPointer("Random suffix string"), - PhoneIsPreferred: models.BoolPointer(false), - EmailIsPreferred: models.BoolPointer(false), - } - - customAddress := models.Address{ - StreetAddress1: "987 Another Street", - } - - customUser := models.User{ - OktaEmail: "test_email@email.com", - } - - serviceMember := factory.BuildServiceMember(suite.DB(), []factory.Customization{ - {Model: customServiceMember}, - {Model: customAddress}, - {Model: customUser}, - }, nil) - - return serviceMember - } - - setupDataForOconusSearchCounselingOffice := func(postalCode string, gbloc string) (models.ReRateArea, models.OconusRateArea, models.UsPostRegionCity, models.DutyLocation) { - contract := testdatagen.FetchOrMakeReContract(suite.DB(), testdatagen.Assertions{}) - - rateAreaCode := uuid.Must(uuid.NewV4()).String()[0:5] - rateArea := testdatagen.FetchOrMakeReRateArea(suite.DB(), testdatagen.Assertions{ - ReRateArea: models.ReRateArea{ - ContractID: contract.ID, - IsOconus: true, - Name: fmt.Sprintf("Alaska-%s", rateAreaCode), - Contract: contract, - }, - }) - suite.NotNil(rateArea) - - us_country, err := models.FetchCountryByCode(suite.DB(), "US") - suite.NotNil(us_country) - suite.Nil(err) - - usprc, err := models.FindByZipCode(suite.AppContextForTest().DB(), postalCode) - suite.NotNil(usprc) - suite.FatalNoError(err) - - oconusRateArea, err := models.FetchOconusRateAreaByCityId(suite.DB(), usprc.ID.String()) - suite.NotNil(oconusRateArea) - suite.Nil(err) - - address := factory.BuildAddress(suite.DB(), []factory.Customization{ - { - Model: models.Address{ - IsOconus: models.BoolPointer(true), - UsPostRegionCityID: &usprc.ID, - }, - }, - }, nil) - - origDutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ - { - Model: models.DutyLocation{ - AddressID: address.ID, - ProvidesServicesCounseling: true, - }, - }, - { - Model: models.TransportationOffice{ - Name: "TEST - PPO", - Gbloc: gbloc, - ProvidesCloseout: true, - }, - }, - }, nil) - suite.MustSave(&origDutyLocation) - - found_duty_location, _ := models.FetchDutyLocation(suite.DB(), origDutyLocation.ID) - - return rateArea, *oconusRateArea, *usprc, found_duty_location - } - - suite.Run("success - findOconusGblocDepartmentIndicator - returns default GBLOC for departmentAffiliation if no specific departmentAffilation mapping is defined", func() { - const fairbanksAlaskaPostalCode = "99790" - _, oconusRateArea, _, dutylocation := setupDataForOconusSearchCounselingOffice(fairbanksAlaskaPostalCode, "JEAT") - - // setup department affiliation to GBLOC mappings - jppsoRegion, err := models.FetchJppsoRegionByCode(suite.DB(), "JEAT") - suite.NotNil(jppsoRegion) - suite.Nil(err) - - gblocAors, err := models.FetchGblocAorsByJppsoCodeRateAreaDept(suite.DB(), jppsoRegion.ID, oconusRateArea.ID, models.DepartmentIndicatorARMY.String()) - suite.NotNil(gblocAors) - suite.Nil(err) - - serviceMember := setupServiceMember(models.AffiliationARMY) - appCtx := suite.AppContextWithSessionForTest(&auth.Session{ - ServiceMemberID: serviceMember.ID, - }) - departmentIndictor, err := findOconusGblocDepartmentIndicator(appCtx, dutylocation, serviceMember.ID) - suite.NotNil(departmentIndictor) - suite.Nil(err) - suite.Nil(departmentIndictor.DepartmentIndicator) - suite.Equal("JEAT", departmentIndictor.Gbloc) - }) - - suite.Run("success - findOconusGblocDepartmentIndicator - returns specific GBLOC for departmentAffiliation when a specific departmentAffilation mapping is defined -- simulate Zone 2 scenerio", func() { - const fairbanksAlaskaPostalCode = "99790" - _, oconusRateArea, _, dutylocation := setupDataForOconusSearchCounselingOffice(fairbanksAlaskaPostalCode, "MBFL") - - // setup department affiliation to GBLOC mappings - jppsoRegion, err := models.FetchJppsoRegionByCode(suite.DB(), "MBFL") - suite.NotNil(jppsoRegion) - suite.Nil(err) - - gblocAors, err := models.FetchGblocAorsByJppsoCodeRateAreaDept(suite.DB(), jppsoRegion.ID, oconusRateArea.ID, models.DepartmentIndicatorAIRANDSPACEFORCE.String()) - suite.NotNil(gblocAors) - suite.Nil(err) - - // loop through and make sure all branches are using it's own dedicated GBLOC and not default - serviceMember := setupServiceMember(models.AffiliationAIRFORCE) - appCtx := suite.AppContextWithSessionForTest(&auth.Session{ - ServiceMemberID: serviceMember.ID, - }) - suite.Nil(err) - departmentIndictor, err := findOconusGblocDepartmentIndicator(appCtx, dutylocation, appCtx.Session().ServiceMemberID) - suite.NotNil(departmentIndictor) - suite.Nil(err) - suite.NotNil(departmentIndictor.DepartmentIndicator) - suite.Equal(models.DepartmentIndicatorAIRANDSPACEFORCE.String(), *departmentIndictor.DepartmentIndicator) - suite.Equal("MBFL", departmentIndictor.Gbloc) - }) - - suite.Run("failure - findOconusGblocDepartmentIndicator - returns error when find service member ID fails", func() { - _, _, _, dutylocation := setupDataForOconusSearchCounselingOffice("99714", "JEAT") - - appCtx := suite.AppContextWithSessionForTest(&auth.Session{ - // create fake service member ID to raise NOT found error - ServiceMemberID: uuid.Must(uuid.NewV4()), - }) - - departmentIndictor, err := findOconusGblocDepartmentIndicator(appCtx, dutylocation, appCtx.Session().ServiceMemberID) - suite.Nil(departmentIndictor) - suite.NotNil(err) - }) - - suite.Run("failure - not found duty location returns error", func() { - appCtx := suite.AppContextWithSessionForTest(&auth.Session{ - ServiceMemberID: uuid.Must(uuid.NewV4()), - }) - unknown_duty_location_id := uuid.Must(uuid.NewV4()) - offices, err := findCounselingOffice(appCtx, unknown_duty_location_id, appCtx.Session().ServiceMemberID) - suite.Nil(offices) - suite.NotNil(err) - }) - - suite.Run("success - offices using default departmentIndicator mapping", func() { - _, oconusRateArea, _, dutylocation := setupDataForOconusSearchCounselingOffice("99619", "MAPS") - - // setup department affiliation to GBLOC mappings - jppsoRegion, err := models.FetchJppsoRegionByCode(suite.DB(), "MAPS") - suite.NotNil(jppsoRegion) - suite.Nil(err) - - gblocAors, err := models.FetchGblocAorsByJppsoCodeRateAreaDept(suite.DB(), jppsoRegion.ID, oconusRateArea.ID, models.DepartmentIndicatorARMY.String()) - suite.NotNil(gblocAors) - suite.Nil(err) - - serviceMember := setupServiceMember(models.AffiliationAIRFORCE) - appCtx := suite.AppContextWithSessionForTest(&auth.Session{ - ServiceMemberID: serviceMember.ID, - }) - - suite.Nil(err) - offices, err := findCounselingOffice(appCtx, dutylocation.ID, appCtx.Session().ServiceMemberID) - suite.NotNil(offices) - suite.Nil(err) - suite.Equal(1, len(offices)) - suite.Equal("TEST - PPO", offices[0].Name) - - // add another transportation office - factory.BuildTransportationOffice(suite.DB(), []factory.Customization{ - { - Model: models.TransportationOffice{ - Name: "TEST - PPO2", - ProvidesCloseout: true, - Gbloc: "MAPS", - }, - }, - }, nil) - offices, err = findCounselingOffice(appCtx, dutylocation.ID, appCtx.Session().ServiceMemberID) - suite.NotNil(offices) - suite.Nil(err) - suite.Equal(2, len(offices)) - }) - + suite.Len(*offices, 2) } func (suite *TransportationOfficeServiceSuite) Test_GetTransportationOffice() { diff --git a/src/components/Customer/EditOrdersForm/EditOrdersForm.jsx b/src/components/Customer/EditOrdersForm/EditOrdersForm.jsx index ab0b538ba7a..017a2f68973 100644 --- a/src/components/Customer/EditOrdersForm/EditOrdersForm.jsx +++ b/src/components/Customer/EditOrdersForm/EditOrdersForm.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { Formik, Field } from 'formik'; import * as Yup from 'yup'; import { Radio, FormGroup, Label, Link as USWDSLink } from '@trussworks/react-uswds'; +import { connect } from 'react-redux'; import { isBooleanFlagEnabled } from '../../../utils/featureFlags'; @@ -16,7 +17,7 @@ import FileUpload from 'components/FileUpload/FileUpload'; import UploadsTable from 'components/UploadsTable/UploadsTable'; import LoadingPlaceholder from 'shared/LoadingPlaceholder'; import SectionWrapper from 'components/Customer/SectionWrapper'; -import { FEATURE_FLAG_KEYS, documentSizeLimitMsg } from 'shared/constants'; +import { FEATURE_FLAG_KEYS, MOVE_STATUSES, documentSizeLimitMsg } from 'shared/constants'; import profileImage from 'scenes/Review/images/profile.png'; import { DropdownArrayOf } from 'types'; import { ExistingUploadsShape } from 'types/uploads'; @@ -26,6 +27,10 @@ import Callout from 'components/Callout'; import { formatLabelReportByDate, dropdownInputOptions, formatYesNoAPIValue } from 'utils/formatters'; import formStyles from 'styles/form.module.scss'; import { showCounselingOffices } from 'services/internalApi'; +import { setShowLoadingSpinner as setShowLoadingSpinnerAction } from 'store/general/actions'; +import { milmoveLogger } from 'utils/milmoveLog'; +import retryPageLoading from 'utils/retryPageLoading'; +import Hint from 'components/Hint'; const EditOrdersForm = ({ createUpload, @@ -36,6 +41,7 @@ const EditOrdersForm = ({ onSubmit, ordersTypeOptions, onCancel, + setShowLoadingSpinner, }) => { const [officeOptions, setOfficeOptions] = useState(null); const [currentDutyLocation, setDutyLocation] = useState(initialValues.origin_duty_location); @@ -93,7 +99,7 @@ const EditOrdersForm = ({ }); const enableDelete = () => { - const isValuePresent = initialValues.move_status === 'DRAFT'; + const isValuePresent = initialValues.move_status === MOVE_STATUSES.DRAFT; return isValuePresent; }; @@ -113,25 +119,40 @@ const EditOrdersForm = ({ }; checkUBFeatureFlag(); }, []); + useEffect(() => { - showCounselingOffices(currentDutyLocation?.id).then((fetchedData) => { - if (fetchedData.body) { - const counselingOffices = fetchedData.body.map((item) => ({ - key: item.id, - value: item.name, - })); - setOfficeOptions(counselingOffices); + const fetchCounselingOffices = async () => { + if (currentDutyLocation?.id && !officeOptions) { + setShowLoadingSpinner(true, null); + try { + const fetchedData = await showCounselingOffices(currentDutyLocation.id); + if (fetchedData.body) { + const counselingOffices = fetchedData.body.map((item) => ({ + key: item.id, + value: item.name, + })); + setOfficeOptions(counselingOffices); + } + } catch (error) { + const { message } = error; + milmoveLogger.error({ message, info: null }); + retryPageLoading(error); + } + setShowLoadingSpinner(false, null); } - }); + }; + fetchCounselingOffices(); + }, [currentDutyLocation.id, officeOptions, setShowLoadingSpinner]); + + useEffect(() => { // Check if either currentDutyLocation or newDutyLocation is OCONUS if (currentDutyLocation?.address?.isOconus || newDutyLocation?.address?.isOconus) { setIsOconusMove(true); } else { setIsOconusMove(false); } + if (currentDutyLocation?.address && newDutyLocation?.address && enableUB) { - // Only if one of the duty locations is OCONUS should accompanied tour and dependent - // age fields display if (isOconusMove && hasDependents) { setShowAccompaniedTourField(true); setShowDependentAgeFields(true); @@ -140,12 +161,13 @@ const EditOrdersForm = ({ setShowDependentAgeFields(false); } } + if (isLoading && finishedFetchingFF) { // If the form is still loading and the FF has finished fetching, // then the form is done loading setIsLoading(false); } - }, [currentDutyLocation, newDutyLocation, isOconusMove, hasDependents, enableUB, finishedFetchingFF, isLoading]); + }, [currentDutyLocation, newDutyLocation, isOconusMove, hasDependents, enableUB, isLoading, finishedFetchingFF]); if (isLoading) { return ; @@ -272,11 +294,6 @@ const EditOrdersForm = ({ /> {currentDutyLocation?.provides_services_counseling && (
- + + Select an origin duty location that most closely represents your current physical location, not + where your shipment will originate, if different. This will allow a nearby transportation office to + assist. +
)} {isRetirementOrSeparation ? ( @@ -521,4 +543,8 @@ EditOrdersForm.defaultProps = { filePondEl: null, }; -export default EditOrdersForm; +const mapDispatchToProps = { + setShowLoadingSpinner: setShowLoadingSpinnerAction, +}; + +export default connect(() => ({}), mapDispatchToProps)(EditOrdersForm); diff --git a/src/components/Customer/EditOrdersForm/EditOrdersForm.stories.jsx b/src/components/Customer/EditOrdersForm/EditOrdersForm.stories.jsx index d36e367b505..0da5575ec99 100644 --- a/src/components/Customer/EditOrdersForm/EditOrdersForm.stories.jsx +++ b/src/components/Customer/EditOrdersForm/EditOrdersForm.stories.jsx @@ -3,6 +3,7 @@ import React from 'react'; import EditOrdersForm from './EditOrdersForm'; import { ORDERS_TYPE } from 'constants/orders'; +import { MockProviders } from 'testUtils'; const testInitialValues = { orders_type: ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION, @@ -101,49 +102,57 @@ const testProps = { }; export const EmptyValues = (argTypes) => ( - + + + ); export const PrefillNoDependents = (argTypes) => ( - + + + ); export const PrefillYesDependents = (argTypes) => ( - + + + ); export const PCSOnly = (argTypes) => ( - + + + ); diff --git a/src/components/Customer/EditOrdersForm/EditOrdersForm.test.jsx b/src/components/Customer/EditOrdersForm/EditOrdersForm.test.jsx index e073ddcc883..c3e2f420cea 100644 --- a/src/components/Customer/EditOrdersForm/EditOrdersForm.test.jsx +++ b/src/components/Customer/EditOrdersForm/EditOrdersForm.test.jsx @@ -9,6 +9,7 @@ import EditOrdersForm from './EditOrdersForm'; import { documentSizeLimitMsg } from 'shared/constants'; import { showCounselingOffices } from 'services/internalApi'; import { ORDERS_TYPE, ORDERS_TYPE_OPTIONS } from 'constants/orders'; +import { MockProviders } from 'testUtils'; jest.setTimeout(60000); @@ -265,7 +266,11 @@ describe('EditOrdersForm component', () => { [/Pay grade/, true, HTMLSelectElement], [/Current duty location/, false, HTMLInputElement], ])('rendering %s and is required is %s', async (formInput, required, inputType) => { - render(); + render( + + + , + ); expect(await screen.findByLabelText(formInput)).toBeInstanceOf(inputType); if (required) { @@ -276,7 +281,11 @@ describe('EditOrdersForm component', () => { it('rendering the upload area', async () => { showCounselingOffices.mockImplementation(() => Promise.resolve({})); - render(); + render( + + + , + ); expect(await screen.findByText(documentSizeLimitMsg)).toBeInTheDocument(); }); @@ -294,7 +303,11 @@ describe('EditOrdersForm component', () => { ])('rendering the %s option', async (selectionOption, expectedValue) => { isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true)); - render(); + render( + + + , + ); const ordersTypeDropdown = await screen.findByLabelText(/Orders type/); expect(ordersTypeDropdown).toBeInstanceOf(HTMLSelectElement); @@ -309,33 +322,35 @@ describe('EditOrdersForm component', () => { it('allows new and current duty location to be the same', async () => { // Render the component render( - + , + new_duty_location: { + name: 'Luke AFB', + provides_services_counseling: false, + address: { isOconus: false }, + }, + counseling_office_id: '3e937c1f-5539-4919-954d-017989130584', + uploaded_orders: [ + { + id: '123', + createdAt: '2020-11-08', + bytes: 1, + url: 'url', + filename: 'Test Upload', + contentType: 'application/pdf', + }, + ], + }} + /> + , ); await waitFor(() => expect(screen.queryByText('Loading, please wait...')).not.toBeInTheDocument()); @@ -361,7 +376,11 @@ describe('EditOrdersForm component', () => { }); it('shows an error message if the form is invalid', async () => { - render(); + render( + + + , + ); const submitButton = await screen.findByRole('button', { name: 'Save' }); const ordersTypeDropdown = screen.getByLabelText(/Orders type/); @@ -379,27 +398,29 @@ describe('EditOrdersForm component', () => { it('submits the form when its valid', async () => { // Not testing the upload interaction, so give uploaded orders to the props. render( - + , + counseling_office_id: '3e937c1f-5539-4919-954d-017989130584', + uploaded_orders: [ + { + id: '123', + createdAt: '2020-11-08', + bytes: 1, + url: 'url', + filename: 'Test Upload', + contentType: 'application/pdf', + }, + ], + }} + /> + , ); await waitFor(() => expect(screen.queryByText('Loading, please wait...')).not.toBeInTheDocument()); @@ -470,7 +491,11 @@ describe('EditOrdersForm component', () => { }); it('implements the onCancel handler when the Cancel button is clicked', async () => { - render(); + render( + + + , + ); const cancelButton = await screen.findByRole('button', { name: 'Cancel' }); @@ -534,7 +559,11 @@ describe('EditOrdersForm component', () => { }; it('pre-fills the inputs', async () => { - render(); + render( + + + , + ); expect(await screen.findByRole('form')).toHaveFormValues({ new_duty_location: 'Yuma AFB', @@ -552,7 +581,11 @@ describe('EditOrdersForm component', () => { }); it('renders the uploads table with an existing upload', async () => { - render(); + render( + + + , + ); await waitFor(() => { expect(screen.queryByText('Test Upload')).toBeInTheDocument(); @@ -628,7 +661,11 @@ describe('EditOrdersForm component', () => { modifiedProps.initialValues[attributeName] = valueToReplaceIt; - render(); + render( + + + , + ); const save = await screen.findByRole('button', { name: 'Save' }); await waitFor(() => { @@ -642,27 +679,29 @@ describe('EditOrdersForm component', () => { it('submits the form when temporary duty orders type is selected', async () => { // Not testing the upload interaction, so give uploaded orders to the props. render( - + , + counseling_office_id: '3e937c1f-5539-4919-954d-017989130584', + uploaded_orders: [ + { + id: '123', + createdAt: '2020-11-08', + bytes: 1, + url: 'url', + filename: 'Test Upload', + contentType: 'application/pdf', + }, + ], + }} + /> + , ); await waitFor(() => expect(screen.queryByText('Loading, please wait...')).not.toBeInTheDocument()); @@ -702,8 +741,11 @@ describe('EditOrdersForm component', () => { it('has dependents is yes and disabled when order type is student travel', async () => { isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true)); - render(); - + render( + + + , + ); await waitFor(() => expect(screen.queryByText('Loading, please wait...')).not.toBeInTheDocument()); await userEvent.selectOptions(screen.getByLabelText(/Orders type/), ORDERS_TYPE.STUDENT_TRAVEL); @@ -719,8 +761,11 @@ describe('EditOrdersForm component', () => { }); it('has dependents is yes and disabled when order type is early return', async () => { - render(); - + render( + + + , + ); await waitFor(() => expect(screen.queryByText('Loading, please wait...')).not.toBeInTheDocument()); await userEvent.selectOptions(screen.getByLabelText(/Orders type/), ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS); @@ -736,8 +781,11 @@ describe('EditOrdersForm component', () => { }); it('has dependents becomes disabled and then re-enabled for order type student travel', async () => { - render(); - + render( + + + , + ); await waitFor(() => expect(screen.queryByText('Loading, please wait...')).not.toBeInTheDocument()); // set order type to perm change and verify the "has dependents" state @@ -771,8 +819,11 @@ describe('EditOrdersForm component', () => { }); it('has dependents becomes disabled and then re-enabled for order type early return', async () => { - render(); - + render( + + + , + ); await waitFor(() => expect(screen.queryByText('Loading, please wait...')).not.toBeInTheDocument()); // set order type to perm change and verify the "has dependents" state diff --git a/src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx b/src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx index 704d3db9953..ca2adac05bd 100644 --- a/src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx +++ b/src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx @@ -105,34 +105,28 @@ const OrdersInfoForm = ({ ordersTypeOptions, initialValues, onSubmit, onBack, se } setShowLoadingSpinner(false, null); } + }; + fetchCounselingOffices(); + }, [counselingOfficeOptions, currentDutyLocation.id, setShowLoadingSpinner]); + + useEffect(() => { + // Check if either currentDutyLocation or newDutyLocation is OCONUS + if (currentDutyLocation?.address?.isOconus || newDutyLocation?.address?.isOconus) { + setIsOconusMove(true); + } else { + setIsOconusMove(false); + } - // Check if either currentDutyLocation or newDutyLocation is OCONUS - if (currentDutyLocation?.address?.isOconus || newDutyLocation?.address?.isOconus) { - setIsOconusMove(true); + if (currentDutyLocation?.address && newDutyLocation?.address && enableUB) { + if (isOconusMove && hasDependents) { + setShowAccompaniedTourField(true); + setShowDependentAgeFields(true); } else { - setIsOconusMove(false); + setShowAccompaniedTourField(false); + setShowDependentAgeFields(false); } - - if (currentDutyLocation?.address && newDutyLocation?.address && enableUB) { - if (isOconusMove && hasDependents) { - setShowAccompaniedTourField(true); - setShowDependentAgeFields(true); - } else { - setShowAccompaniedTourField(false); - setShowDependentAgeFields(false); - } - } - }; - fetchCounselingOffices(); - }, [ - currentDutyLocation, - newDutyLocation, - isOconusMove, - hasDependents, - enableUB, - setShowLoadingSpinner, - counselingOfficeOptions, - ]); + } + }, [currentDutyLocation, newDutyLocation, isOconusMove, hasDependents, enableUB]); useEffect(() => { const fetchData = async () => { @@ -260,11 +254,6 @@ const OrdersInfoForm = ({ ordersTypeOptions, initialValues, onSubmit, onBack, se /> {currentDutyLocation.provides_services_counseling && (
- + + Select an origin duty location that most closely represents your current physical location, not + where your shipment will originate, if different. This will allow a nearby transportation office to + assist you. +
)} {isRetirementOrSeparation ? ( diff --git a/src/components/Office/AddOrdersForm/AddOrdersForm.jsx b/src/components/Office/AddOrdersForm/AddOrdersForm.jsx index a2c6be4ff4d..4df4e5774a4 100644 --- a/src/components/Office/AddOrdersForm/AddOrdersForm.jsx +++ b/src/components/Office/AddOrdersForm/AddOrdersForm.jsx @@ -22,6 +22,7 @@ import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextFi import formStyles from 'styles/form.module.scss'; import ConnectedFlashMessage from 'containers/FlashMessage/FlashMessage'; import { showCounselingOffices } from 'services/ghcApi'; +import Hint from 'components/Hint'; let originMeta; let newDutyMeta = ''; @@ -217,11 +218,6 @@ const AddOrdersForm = ({ /> {currentDutyLocation.provides_services_counseling && (
- + + Select an origin duty location that most closely represents the customers current physical location, + not where their shipment will originate, if different. This will allow a nearby transportation + office to assist them. +
)} diff --git a/src/pages/MyMove/AddOrders.test.jsx b/src/pages/MyMove/AddOrders.test.jsx index fa87055adfd..eddd30dc4fd 100644 --- a/src/pages/MyMove/AddOrders.test.jsx +++ b/src/pages/MyMove/AddOrders.test.jsx @@ -9,7 +9,7 @@ import { createOrders, getServiceMember, showCounselingOffices } from 'services/ import { renderWithProviders } from 'testUtils'; import { customerRoutes, generalRoutes } from 'constants/routes'; import { selectCanAddOrders, selectServiceMemberFromLoggedInUser } from 'store/entities/selectors'; -import { setCanAddOrders, setMoveId } from 'store/general/actions'; +import { setCanAddOrders, setMoveId, setShowLoadingSpinner } from 'store/general/actions'; import { isBooleanFlagEnabled } from 'utils/featureFlags'; import { ORDERS_TYPE } from 'constants/orders'; @@ -54,6 +54,11 @@ jest.mock('store/general/actions', () => ({ type: '', payload: '', })), + setShowLoadingSpinner: jest.fn().mockImplementation(() => ({ + type: '', + showSpinner: false, + loadingSpinnerMessage: '', + })), })); const mockNavigate = jest.fn(); @@ -296,6 +301,23 @@ describe('Add Orders page', () => { ).not.toBeInTheDocument(); }); + it('calls the loading spinner when a current duty location is selected', async () => { + isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true)); + selectServiceMemberFromLoggedInUser.mockImplementation(() => serviceMember); + renderWithProviders(, { + path: customerRoutes.ORDERS_ADD_PATH, + }); + + await screen.findByRole('heading', { level: 1, name: 'Tell us about your move orders' }); + // Select a CONUS current duty location + await userEvent.type(screen.getByLabelText(/Current duty location/), 'AFB', { delay: 100 }); + const selectedOptionCurrent = await screen.findByText(/Altus/); + await userEvent.click(selectedOptionCurrent); + await waitFor(() => { + expect(setShowLoadingSpinner).toHaveBeenCalled(); + }); + }); + it('does not render the input boxes for number of dependents over or under 12 if both locations are CONUS', async () => { selectServiceMemberFromLoggedInUser.mockImplementation(() => serviceMember); renderWithProviders(, { diff --git a/src/pages/MyMove/EditOrders.test.jsx b/src/pages/MyMove/EditOrders.test.jsx index 666e44b65e2..5542da5bddf 100644 --- a/src/pages/MyMove/EditOrders.test.jsx +++ b/src/pages/MyMove/EditOrders.test.jsx @@ -15,6 +15,7 @@ import { } from 'store/entities/selectors'; import { isBooleanFlagEnabled } from 'utils/featureFlags'; import { ORDERS_TYPE } from 'constants/orders'; +import { setShowLoadingSpinner } from 'store/general/actions'; const mockNavigate = jest.fn(); jest.mock('react-router-dom', () => ({ @@ -55,6 +56,15 @@ jest.mock('utils/featureFlags', () => ({ isBooleanFlagEnabled: jest.fn().mockImplementation(() => Promise.resolve(false)), })); +jest.mock('store/general/actions', () => ({ + ...jest.requireActual('store/general/actions'), + setShowLoadingSpinner: jest.fn().mockImplementation(() => ({ + type: '', + showSpinner: false, + loadingSpinnerMessage: '', + })), +})); + describe('EditOrders Page', () => { const testProps = { serviceMember: { @@ -440,5 +450,23 @@ describe('EditOrders Page', () => { }); }); + it('calls the loading spinner on page load', async () => { + renderWithProviders(, { + path: customerRoutes.ORDERS_EDIT_PATH, + params: { moveId: 'testMoveId', orderId: 'testOrders1' }, + }); + + // calls the loading spinner on load + await waitFor(() => { + expect(setShowLoadingSpinner).toHaveBeenCalled(); + }); + + const h1 = await screen.findByRole('heading', { name: 'Orders', level: 1 }); + expect(h1).toBeInTheDocument(); + + const editOrdersHeader = await screen.findByRole('heading', { name: 'Edit Orders:', level: 2 }); + expect(editOrdersHeader).toBeInTheDocument(); + }); + afterEach(jest.clearAllMocks); }); From b6322f5e7f7146a8e4579a77949b8adfc6322c76 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Mon, 24 Feb 2025 18:26:44 +0000 Subject: [PATCH 21/26] adding functions and tests back in due to code on INT but not MAIN using it --- .../transportation_office_fetcher.go | 172 +++++++++++++++ .../transportation_office_fetcher_test.go | 207 ++++++++++++++++++ 2 files changed, 379 insertions(+) diff --git a/pkg/services/transportation_office/transportation_office_fetcher.go b/pkg/services/transportation_office/transportation_office_fetcher.go index e178ef1adc5..2ad7681ea29 100644 --- a/pkg/services/transportation_office/transportation_office_fetcher.go +++ b/pkg/services/transportation_office/transportation_office_fetcher.go @@ -2,6 +2,7 @@ package transportationoffice import ( "database/sql" + "fmt" "github.com/gofrs/uuid" "github.com/pkg/errors" @@ -12,6 +13,12 @@ import ( "github.com/transcom/mymove/pkg/services" ) +type oconusGblocDepartmentIndicator struct { + Gbloc string `db:"gbloc" rw:"r"` + RateAreaName string `db:"rate_area_name" rw:"r"` + DepartmentIndicator *string `db:"department_indicator" rw:"r"` +} + type transportationOfficesFetcher struct { } @@ -142,3 +149,168 @@ func (o transportationOfficesFetcher) GetCounselingOffices(appCtx appcontext.App return &officeList, nil } + +// return all the transportation offices in the GBLOC of the given duty location where provides_services_counseling = true +// serviceMemberID is only provided when this function is called by the office handler +func findCounselingOffice(appCtx appcontext.AppContext, dutyLocationID uuid.UUID, serviceMemberID uuid.UUID) (models.TransportationOffices, error) { + var officeList []models.TransportationOffice + + duty_location, err := models.FetchDutyLocation(appCtx.DB(), dutyLocationID) + if err != nil { + return officeList, err + } + + var sqlQuery string + + // ******************************** + // Find for oconus duty location + // ******************************** + if *duty_location.Address.IsOconus { + gblocDepartmentIndicator, err := findOconusGblocDepartmentIndicator(appCtx, duty_location, serviceMemberID) + if err != nil { + return officeList, err + } + + sqlQuery = ` + with counseling_offices as ( + SELECT transportation_offices.id, transportation_offices.name, transportation_offices.address_id as counseling_address, + substring(a.postal_code, 1,3 ) as origin_zip, substring(a2.postal_code, 1,3 ) as dest_zip + FROM duty_locations + JOIN addresses a on duty_locations.address_id = a.id + JOIN v_locations v on (a.us_post_region_cities_id = v.uprc_id or v.uprc_id is null) + and a.country_id = v.country_id + JOIN re_oconus_rate_areas r on r.us_post_region_cities_id = v.uprc_id + JOIN gbloc_aors on gbloc_aors.oconus_rate_area_id = r.id + JOIN jppso_regions j on gbloc_aors.jppso_regions_id = j.id + JOIN transportation_offices on j.code = transportation_offices.gbloc + join addresses a2 on a2.id = transportation_offices.address_id + WHERE duty_locations.provides_services_counseling = true and duty_locations.id = $1 and j.code = $2 + and transportation_offices.provides_ppm_closeout = true + ) + SELECT counseling_offices.id, counseling_offices.name + FROM counseling_offices + JOIN addresses cnsl_address on counseling_offices.counseling_address = cnsl_address.id + LEFT JOIN zip3_distances ON ( + (substring(cnsl_address.postal_code,1 ,3) = zip3_distances.to_zip3 + AND counseling_offices.origin_zip = zip3_distances.from_zip3) + OR + (substring(cnsl_address.postal_code,1 ,3) = zip3_distances.from_zip3 + AND counseling_offices.origin_zip = zip3_distances.to_zip3) + ) + group by counseling_offices.id, counseling_offices.name, zip3_distances.distance_miles + ORDER BY coalesce(zip3_distances.distance_miles,0) asc` + + query := appCtx.DB().Q().RawQuery(sqlQuery, dutyLocationID, gblocDepartmentIndicator.Gbloc) + if err := query.All(&officeList); err != nil { + if errors.Cause(err).Error() != models.RecordNotFoundErrorString { + return officeList, err + } + } + return officeList, nil + } + + // ******************************** + // Find for conus duty location + // ******************************** + sqlQuery = ` + with counseling_offices as ( + SELECT transportation_offices.id, transportation_offices.name, transportation_offices.address_id as counseling_address, substring(addresses.postal_code, 1,3 ) as pickup_zip + FROM postal_code_to_gblocs + JOIN addresses on postal_code_to_gblocs.postal_code = addresses.postal_code + JOIN duty_locations on addresses.id = duty_locations.address_id + JOIN transportation_offices on postal_code_to_gblocs.gbloc = transportation_offices.gbloc + WHERE duty_locations.provides_services_counseling = true and duty_locations.id = $1 + ) + SELECT counseling_offices.id, counseling_offices.name + FROM counseling_offices + JOIN duty_locations duty_locations2 on counseling_offices.id = duty_locations2.transportation_office_id + JOIN addresses on counseling_offices.counseling_address = addresses.id + JOIN re_us_post_regions on addresses.postal_code = re_us_post_regions.uspr_zip_id + LEFT JOIN zip3_distances ON ( + (re_us_post_regions.zip3 = zip3_distances.to_zip3 + AND counseling_offices.pickup_zip = zip3_distances.from_zip3) + OR + (re_us_post_regions.zip3 = zip3_distances.from_zip3 + AND counseling_offices.pickup_zip = zip3_distances.to_zip3) + ) + WHERE duty_locations2.provides_services_counseling = true + group by counseling_offices.id, counseling_offices.name, zip3_distances.distance_miles + ORDER BY coalesce(zip3_distances.distance_miles,0), counseling_offices.name asc` + + query := appCtx.DB().Q().RawQuery(sqlQuery, dutyLocationID) + if err := query.All(&officeList); err != nil { + if errors.Cause(err).Error() != models.RecordNotFoundErrorString { + return officeList, err + } + } + + return officeList, nil +} + +func findOconusGblocDepartmentIndicator(appCtx appcontext.AppContext, dutyLocation models.DutyLocation, serviceMemberID uuid.UUID) (*oconusGblocDepartmentIndicator, error) { + serviceMember, err := models.FetchServiceMember(appCtx.DB(), serviceMemberID) + if err != nil { + return nil, err + } + + var oconusGblocDepartmentIndicator []oconusGblocDepartmentIndicator + + sqlQuery := ` + select j.code gbloc, r.name rate_area_name, g.department_indicator + from addresses a, + v_locations v, + re_oconus_rate_areas o, + re_rate_areas r, + jppso_regions j, + gbloc_aors g + where a.id = $1 + and a.us_post_region_cities_id = v.uprc_id + and v.uprc_id = o.us_post_region_cities_id + and o.rate_area_id = r.id + and o.id = g.oconus_rate_area_id + and j.id = g.jppso_regions_id` + + query := appCtx.DB().Q().RawQuery(sqlQuery, dutyLocation.Address.ID) + err = query.All(&oconusGblocDepartmentIndicator) + if err != nil { + return nil, err + } + + // Determine departmentIndicator based on service member's affiliation + var departmentIndicator *string = nil + if serviceMember.Affiliation != nil && (*serviceMember.Affiliation == models.AffiliationAIRFORCE || *serviceMember.Affiliation == models.AffiliationSPACEFORCE) { + departmentIndicator = models.StringPointer(models.DepartmentIndicatorAIRANDSPACEFORCE.String()) + } else if serviceMember.Affiliation != nil && (*serviceMember.Affiliation == models.AffiliationNAVY || *serviceMember.Affiliation == models.AffiliationMARINES) { + departmentIndicator = models.StringPointer(models.DepartmentIndicatorNAVYANDMARINES.String()) + } else if serviceMember.Affiliation != nil && (*serviceMember.Affiliation == models.AffiliationARMY) { + departmentIndicator = models.StringPointer(models.DepartmentIndicatorARMY.String()) + } else if serviceMember.Affiliation != nil && (*serviceMember.Affiliation == models.AffiliationCOASTGUARD) { + departmentIndicator = models.StringPointer(models.DepartmentIndicatorCOASTGUARD.String()) + } + + // Is there a matching GBLOC for duty location address specifically for user's affiliation and duty location Zone(I-V)? + // sample oconusGblocAffiliationInfo[]: + // Gbloc RateAreaName DepartmentIndicator + // JEAT Alaska (Zone) II NULL (default) + // MBFL Alaska (Zone) II AIR_AND_SPACE_FORCE + for _, info := range oconusGblocDepartmentIndicator { + if (info.DepartmentIndicator != nil && departmentIndicator != nil) && (*info.DepartmentIndicator == *departmentIndicator) { + appCtx.Logger().Debug(fmt.Sprintf("Found specific department match -- serviceMember.Affiliation: %s, DutyLocaton: %s, GBLOC: %s, departmentIndicator: %s, RateAreaName: %s, dutyLocation.Address.ID: %s", + serviceMember.Affiliation, dutyLocation.Name, info.Gbloc, *departmentIndicator, info.RateAreaName, dutyLocation.Address.ID)) + return &info, nil + } + } + + // These were no departmentIndicator specific GBLOC for duty location -- return default GBLOC + for _, info := range oconusGblocDepartmentIndicator { + if info.DepartmentIndicator == nil { + appCtx.Logger().Debug(fmt.Sprintf("Did not find specific department match, return default -- serviceMember.Affiliation: %s, DutyLocaton: %s, GBLOC: %s, departmentIndicator: %s, RateAreaName: %s, dutyLocation.Address.ID: %s", + serviceMember.Affiliation, dutyLocation.Name, info.Gbloc, "NIL/NULL", info.RateAreaName, dutyLocation.Address.ID)) + return &info, nil + } + } + + // There is no default and department specific oconusGblocDepartmentIndicator. There is nothing in system to support. This should never happen. + return nil, apperror.NewImplementationError(fmt.Sprintf("Error: Cannot determine GBLOC -- serviceMember.Affiliation: %s, DutyLocaton: %s, departmentIndicator: %s, dutyLocation.Address.ID: %s", + serviceMember.Affiliation, dutyLocation.Name, *departmentIndicator, dutyLocation.Address.ID)) +} diff --git a/pkg/services/transportation_office/transportation_office_fetcher_test.go b/pkg/services/transportation_office/transportation_office_fetcher_test.go index 801ad821e96..fdf7606860c 100644 --- a/pkg/services/transportation_office/transportation_office_fetcher_test.go +++ b/pkg/services/transportation_office/transportation_office_fetcher_test.go @@ -1,14 +1,17 @@ package transportationoffice import ( + "fmt" "testing" "github.com/gofrs/uuid" "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/auth" "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/testdatagen" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -191,6 +194,210 @@ func (suite *TransportationOfficeServiceSuite) Test_FindCounselingOffices() { suite.Len(*offices, 2) } +func (suite *TransportationOfficeServiceSuite) Test_Oconus_AK_FindCounselingOffices() { + setupServiceMember := func(serviceMemberAffiliation models.ServiceMemberAffiliation) models.ServiceMember { + customServiceMember := models.ServiceMember{ + FirstName: models.StringPointer("Gregory"), + LastName: models.StringPointer("Van der Heide"), + Telephone: models.StringPointer("999-999-9999"), + SecondaryTelephone: models.StringPointer("123-555-9999"), + PersonalEmail: models.StringPointer("peyton@example.com"), + Edipi: models.StringPointer("1000011111"), + Affiliation: &serviceMemberAffiliation, + Suffix: models.StringPointer("Random suffix string"), + PhoneIsPreferred: models.BoolPointer(false), + EmailIsPreferred: models.BoolPointer(false), + } + + customAddress := models.Address{ + StreetAddress1: "987 Another Street", + } + + customUser := models.User{ + OktaEmail: "test_email@email.com", + } + + serviceMember := factory.BuildServiceMember(suite.DB(), []factory.Customization{ + {Model: customServiceMember}, + {Model: customAddress}, + {Model: customUser}, + }, nil) + + return serviceMember + } + + setupDataForOconusSearchCounselingOffice := func(postalCode string, gbloc string) (models.ReRateArea, models.OconusRateArea, models.UsPostRegionCity, models.DutyLocation) { + contract := testdatagen.FetchOrMakeReContract(suite.DB(), testdatagen.Assertions{}) + + rateAreaCode := uuid.Must(uuid.NewV4()).String()[0:5] + rateArea := testdatagen.FetchOrMakeReRateArea(suite.DB(), testdatagen.Assertions{ + ReRateArea: models.ReRateArea{ + ContractID: contract.ID, + IsOconus: true, + Name: fmt.Sprintf("Alaska-%s", rateAreaCode), + Contract: contract, + }, + }) + suite.NotNil(rateArea) + + us_country, err := models.FetchCountryByCode(suite.DB(), "US") + suite.NotNil(us_country) + suite.Nil(err) + + usprc, err := models.FindByZipCode(suite.AppContextForTest().DB(), postalCode) + suite.NotNil(usprc) + suite.FatalNoError(err) + + oconusRateArea, err := models.FetchOconusRateAreaByCityId(suite.DB(), usprc.ID.String()) + suite.NotNil(oconusRateArea) + suite.Nil(err) + + address := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + IsOconus: models.BoolPointer(true), + UsPostRegionCityID: &usprc.ID, + }, + }, + }, nil) + + origDutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ + { + Model: models.DutyLocation{ + AddressID: address.ID, + ProvidesServicesCounseling: true, + }, + }, + { + Model: models.TransportationOffice{ + Name: "TEST - PPO", + Gbloc: gbloc, + ProvidesCloseout: true, + }, + }, + }, nil) + suite.MustSave(&origDutyLocation) + + found_duty_location, _ := models.FetchDutyLocation(suite.DB(), origDutyLocation.ID) + + return rateArea, *oconusRateArea, *usprc, found_duty_location + } + + suite.Run("success - findOconusGblocDepartmentIndicator - returns default GBLOC for departmentAffiliation if no specific departmentAffilation mapping is defined", func() { + const fairbanksAlaskaPostalCode = "99790" + _, oconusRateArea, _, dutylocation := setupDataForOconusSearchCounselingOffice(fairbanksAlaskaPostalCode, "JEAT") + + // setup department affiliation to GBLOC mappings + jppsoRegion, err := models.FetchJppsoRegionByCode(suite.DB(), "JEAT") + suite.NotNil(jppsoRegion) + suite.Nil(err) + + gblocAors, err := models.FetchGblocAorsByJppsoCodeRateAreaDept(suite.DB(), jppsoRegion.ID, oconusRateArea.ID, models.DepartmentIndicatorARMY.String()) + suite.NotNil(gblocAors) + suite.Nil(err) + + serviceMember := setupServiceMember(models.AffiliationARMY) + appCtx := suite.AppContextWithSessionForTest(&auth.Session{ + ServiceMemberID: serviceMember.ID, + }) + departmentIndictor, err := findOconusGblocDepartmentIndicator(appCtx, dutylocation, serviceMember.ID) + suite.NotNil(departmentIndictor) + suite.Nil(err) + suite.Nil(departmentIndictor.DepartmentIndicator) + suite.Equal("JEAT", departmentIndictor.Gbloc) + }) + + suite.Run("success - findOconusGblocDepartmentIndicator - returns specific GBLOC for departmentAffiliation when a specific departmentAffilation mapping is defined -- simulate Zone 2 scenerio", func() { + const fairbanksAlaskaPostalCode = "99790" + _, oconusRateArea, _, dutylocation := setupDataForOconusSearchCounselingOffice(fairbanksAlaskaPostalCode, "MBFL") + + // setup department affiliation to GBLOC mappings + jppsoRegion, err := models.FetchJppsoRegionByCode(suite.DB(), "MBFL") + suite.NotNil(jppsoRegion) + suite.Nil(err) + + gblocAors, err := models.FetchGblocAorsByJppsoCodeRateAreaDept(suite.DB(), jppsoRegion.ID, oconusRateArea.ID, models.DepartmentIndicatorAIRANDSPACEFORCE.String()) + suite.NotNil(gblocAors) + suite.Nil(err) + + // loop through and make sure all branches are using it's own dedicated GBLOC and not default + serviceMember := setupServiceMember(models.AffiliationAIRFORCE) + appCtx := suite.AppContextWithSessionForTest(&auth.Session{ + ServiceMemberID: serviceMember.ID, + }) + suite.Nil(err) + departmentIndictor, err := findOconusGblocDepartmentIndicator(appCtx, dutylocation, appCtx.Session().ServiceMemberID) + suite.NotNil(departmentIndictor) + suite.Nil(err) + suite.NotNil(departmentIndictor.DepartmentIndicator) + suite.Equal(models.DepartmentIndicatorAIRANDSPACEFORCE.String(), *departmentIndictor.DepartmentIndicator) + suite.Equal("MBFL", departmentIndictor.Gbloc) + }) + + suite.Run("failure - findOconusGblocDepartmentIndicator - returns error when find service member ID fails", func() { + _, _, _, dutylocation := setupDataForOconusSearchCounselingOffice("99714", "JEAT") + + appCtx := suite.AppContextWithSessionForTest(&auth.Session{ + // create fake service member ID to raise NOT found error + ServiceMemberID: uuid.Must(uuid.NewV4()), + }) + + departmentIndictor, err := findOconusGblocDepartmentIndicator(appCtx, dutylocation, appCtx.Session().ServiceMemberID) + suite.Nil(departmentIndictor) + suite.NotNil(err) + }) + + suite.Run("failure - not found duty location returns error", func() { + appCtx := suite.AppContextWithSessionForTest(&auth.Session{ + ServiceMemberID: uuid.Must(uuid.NewV4()), + }) + unknown_duty_location_id := uuid.Must(uuid.NewV4()) + offices, err := findCounselingOffice(appCtx, unknown_duty_location_id, appCtx.Session().ServiceMemberID) + suite.Nil(offices) + suite.NotNil(err) + }) + + suite.Run("success - offices using default departmentIndicator mapping", func() { + _, oconusRateArea, _, dutylocation := setupDataForOconusSearchCounselingOffice("99619", "MAPS") + + // setup department affiliation to GBLOC mappings + jppsoRegion, err := models.FetchJppsoRegionByCode(suite.DB(), "MAPS") + suite.NotNil(jppsoRegion) + suite.Nil(err) + + gblocAors, err := models.FetchGblocAorsByJppsoCodeRateAreaDept(suite.DB(), jppsoRegion.ID, oconusRateArea.ID, models.DepartmentIndicatorARMY.String()) + suite.NotNil(gblocAors) + suite.Nil(err) + + serviceMember := setupServiceMember(models.AffiliationAIRFORCE) + appCtx := suite.AppContextWithSessionForTest(&auth.Session{ + ServiceMemberID: serviceMember.ID, + }) + + suite.Nil(err) + offices, err := findCounselingOffice(appCtx, dutylocation.ID, appCtx.Session().ServiceMemberID) + suite.NotNil(offices) + suite.Nil(err) + suite.Equal(1, len(offices)) + suite.Equal("TEST - PPO", offices[0].Name) + + // add another transportation office + factory.BuildTransportationOffice(suite.DB(), []factory.Customization{ + { + Model: models.TransportationOffice{ + Name: "TEST - PPO2", + ProvidesCloseout: true, + Gbloc: "MAPS", + }, + }, + }, nil) + offices, err = findCounselingOffice(appCtx, dutylocation.ID, appCtx.Session().ServiceMemberID) + suite.NotNil(offices) + suite.Nil(err) + suite.Equal(2, len(offices)) + }) +} + func (suite *TransportationOfficeServiceSuite) Test_GetTransportationOffice() { suite.toFetcher = NewTransportationOfficesFetcher() transportationOffice1 := factory.BuildTransportationOffice(suite.DB(), []factory.Customization{ From a265b88242dab94dfeafcff55a837e226b7a3951 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Tue, 25 Feb 2025 15:03:09 +0000 Subject: [PATCH 22/26] initial commit, added migrations, updated swagger, go structs, populating value on shipment creation --- .circleci/config.yml | 1 + .envrc | 1 + config/env/demo.app-client-tls.env | 1 + config/env/demo.app.env | 1 + config/env/exp.app-client-tls.env | 1 + config/env/exp.app.env | 1 + config/env/loadtest.app-client-tls.env | 1 + config/env/loadtest.app.env | 1 + config/env/prd.app-client-tls.env | 1 + config/env/prd.app.env | 1 + config/env/stg.app-client-tls.env | 1 + config/env/stg.app.env | 1 + .../flipt/storage/development.features.yaml | 16 ++++ .../20250224200700_tbl_ppm_shipments.up.sql | 3 + ...20250224202726_ty_ppm_shipment_type.up.sql | 11 +++ ...50224202738_ty_moving_expenses_type.up.sql | 2 + migrations/app/ddl_tables_manifest.txt | 1 + migrations/app/ddl_types_manifest.txt | 2 + pkg/factory/ppm_shipment_factory.go | 1 + pkg/gen/ghcapi/embedded_spec.go | 28 ++++++ pkg/gen/ghcmessages/p_p_m_shipment.go | 46 ++++++++++ pkg/gen/ghcmessages/p_p_m_type.go | 92 +++++++++++++++++++ pkg/gen/internalapi/embedded_spec.go | 28 ++++++ pkg/gen/internalmessages/p_p_m_shipment.go | 46 ++++++++++ pkg/gen/internalmessages/p_p_m_type.go | 92 +++++++++++++++++++ pkg/models/moving_expense.go | 2 + pkg/models/ppm_shipment.go | 13 +++ .../ppmshipment/ppm_shipment_creator.go | 5 + .../ppmshipment/ppm_shipment_creator_test.go | 1 + src/shared/constants.js | 13 +++ swagger-def/definitions/PPMShipment.yaml | 2 + swagger-def/definitions/PPMType.yaml | 8 ++ swagger/ghc.yaml | 11 +++ swagger/internal.yaml | 11 +++ 34 files changed, 446 insertions(+) create mode 100644 migrations/app/ddl_migrations/ddl_tables/20250224200700_tbl_ppm_shipments.up.sql create mode 100644 migrations/app/ddl_migrations/ddl_types/20250224202726_ty_ppm_shipment_type.up.sql create mode 100644 migrations/app/ddl_migrations/ddl_types/20250224202738_ty_moving_expenses_type.up.sql create mode 100644 pkg/gen/ghcmessages/p_p_m_type.go create mode 100644 pkg/gen/internalmessages/p_p_m_type.go create mode 100644 swagger-def/definitions/PPMType.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml index 7a4fb382a9e..bb76fc8fd5f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -883,6 +883,7 @@ commands: export ENVIRONMENT=test export FEATURE_FLAG_MULTI_MOVE=true export FEATURE_FLAG_PPM=true + export FEATURE_FLAG_PPM_SPR=false export FEATURE_FLAG_NTS=true export FEATURE_FLAG_NTSR=true export FEATURE_FLAG_BOAT=true diff --git a/.envrc b/.envrc index beb0b08ea1f..22374409701 100644 --- a/.envrc +++ b/.envrc @@ -166,6 +166,7 @@ export FEATURE_FLAG_QUEUE_MANAGEMENT=true # Feature flags to disable certain shipment types export FEATURE_FLAG_PPM=true +export FEATURE_FLAG_PPM_SPR=false export FEATURE_FLAG_NTS=true export FEATURE_FLAG_NTSR=true export FEATURE_FLAG_BOAT=true diff --git a/config/env/demo.app-client-tls.env b/config/env/demo.app-client-tls.env index 9990552dff2..5989ccfd533 100644 --- a/config/env/demo.app-client-tls.env +++ b/config/env/demo.app-client-tls.env @@ -32,6 +32,7 @@ TELEMETRY_USE_XRAY_ID=true TLS_ENABLED=true FEATURE_FLAG_MULTI_MOVE=false FEATURE_FLAG_PPM=true +FEATURE_FLAG_PPM_SPR=false FEATURE_FLAG_NTS=true FEATURE_FLAG_NTSR=true FEATURE_FLAG_BOAT=false diff --git a/config/env/demo.app.env b/config/env/demo.app.env index fae5faf098c..2621c72fb7a 100644 --- a/config/env/demo.app.env +++ b/config/env/demo.app.env @@ -37,6 +37,7 @@ SERVE_API_SUPPORT=true FEATURE_FLAG_MULTI_MOVE=false FEATURE_FLAG_COUNSELOR_MOVE_CREATE=false FEATURE_FLAG_PPM=true +FEATURE_FLAG_PPM_SPR=false FEATURE_FLAG_NTS=true FEATURE_FLAG_NTSR=true FEATURE_FLAG_BOAT=false diff --git a/config/env/exp.app-client-tls.env b/config/env/exp.app-client-tls.env index 85f315e3173..706d0d7d7eb 100644 --- a/config/env/exp.app-client-tls.env +++ b/config/env/exp.app-client-tls.env @@ -32,6 +32,7 @@ TELEMETRY_ENDPOINT=localhost:4317 TELEMETRY_USE_XRAY_ID=false FEATURE_FLAG_MULTI_MOVE=false FEATURE_FLAG_PPM=true +FEATURE_FLAG_PPM_SPR=false FEATURE_FLAG_NTS=true FEATURE_FLAG_NTSR=true FEATURE_FLAG_BOAT=false diff --git a/config/env/exp.app.env b/config/env/exp.app.env index 43bb56f3c19..c425328db35 100644 --- a/config/env/exp.app.env +++ b/config/env/exp.app.env @@ -37,6 +37,7 @@ TELEMETRY_USE_XRAY_ID=true FEATURE_FLAG_MULTI_MOVE=true FEATURE_FLAG_COUNSELOR_MOVE_CREATE=false FEATURE_FLAG_PPM=true +FEATURE_FLAG_PPM_SPR=false FEATURE_FLAG_NTS=true FEATURE_FLAG_NTSR=true FEATURE_FLAG_BOAT=false diff --git a/config/env/loadtest.app-client-tls.env b/config/env/loadtest.app-client-tls.env index b26524393e3..7f6b5d92528 100644 --- a/config/env/loadtest.app-client-tls.env +++ b/config/env/loadtest.app-client-tls.env @@ -30,6 +30,7 @@ TELEMETRY_USE_XRAY_ID=true TLS_ENABLED=true FEATURE_FLAG_MULTI_MOVE=false FEATURE_FLAG_PPM=true +FEATURE_FLAG_PPM_SPR=false FEATURE_FLAG_NTS=true FEATURE_FLAG_NTSR=true FEATURE_FLAG_BOAT=false diff --git a/config/env/loadtest.app.env b/config/env/loadtest.app.env index 6f2870f3826..1db3e2ea2ed 100644 --- a/config/env/loadtest.app.env +++ b/config/env/loadtest.app.env @@ -35,6 +35,7 @@ SERVE_API_SUPPORT=true FEATURE_FLAG_MULTI_MOVE=false FEATURE_FLAG_COUNSELOR_MOVE_CREATE=false FEATURE_FLAG_PPM=true +FEATURE_FLAG_PPM_SPR=false FEATURE_FLAG_NTS=true FEATURE_FLAG_NTSR=true FEATURE_FLAG_BOAT=false diff --git a/config/env/prd.app-client-tls.env b/config/env/prd.app-client-tls.env index 9b754221581..c9d4a6228ed 100644 --- a/config/env/prd.app-client-tls.env +++ b/config/env/prd.app-client-tls.env @@ -29,6 +29,7 @@ TELEMETRY_USE_XRAY_ID=true TLS_ENABLED=true FEATURE_FLAG_MULTI_MOVE=false FEATURE_FLAG_PPM=false +FEATURE_FLAG_PPM_SPR=false FEATURE_FLAG_NTS=false FEATURE_FLAG_NTSR=false FEATURE_FLAG_BOAT=false diff --git a/config/env/prd.app.env b/config/env/prd.app.env index be956f4a530..8d2fbc7c7a2 100644 --- a/config/env/prd.app.env +++ b/config/env/prd.app.env @@ -36,6 +36,7 @@ SERVE_API_SUPPORT=false FEATURE_FLAG_MULTI_MOVE=false FEATURE_FLAG_COUNSELOR_MOVE_CREATE=false FEATURE_FLAG_PPM=false +FEATURE_FLAG_PPM_SPR=false FEATURE_FLAG_NTS=false FEATURE_FLAG_NTSR=false FEATURE_FLAG_BOAT=false diff --git a/config/env/stg.app-client-tls.env b/config/env/stg.app-client-tls.env index 7047aafebea..067e1e9bdd2 100644 --- a/config/env/stg.app-client-tls.env +++ b/config/env/stg.app-client-tls.env @@ -31,6 +31,7 @@ TELEMETRY_USE_XRAY_ID=true TLS_ENABLED=true FEATURE_FLAG_MULTI_MOVE=false FEATURE_FLAG_PPM=true +FEATURE_FLAG_PPM_SPR=false FEATURE_FLAG_NTS=true FEATURE_FLAG_NTSR=true FEATURE_FLAG_BOAT=false diff --git a/config/env/stg.app.env b/config/env/stg.app.env index 33ba71851ed..3228ea40103 100644 --- a/config/env/stg.app.env +++ b/config/env/stg.app.env @@ -37,6 +37,7 @@ SERVE_API_SUPPORT=true FEATURE_FLAG_MULTI_MOVE=false FEATURE_FLAG_COUNSELOR_MOVE_CREATE =false FEATURE_FLAG_PPM=true +FEATURE_FLAG_PPM_SPR=false FEATURE_FLAG_NTS=true FEATURE_FLAG_NTSR=true FEATURE_FLAG_BOAT=false diff --git a/config/flipt/storage/development.features.yaml b/config/flipt/storage/development.features.yaml index 224f763e922..cde89f09329 100644 --- a/config/flipt/storage/development.features.yaml +++ b/config/flipt/storage/development.features.yaml @@ -73,6 +73,14 @@ flags: - segment: key: mil-app value: false + - key: enable_hawaii + name: Enable Hawaii feature flag + type: BOOLEAN_FLAG_TYPE + enabled: false + rollouts: + - segment: + key: mil-app + value: false - key: okta_dodid_input name: Customer DODID input being pulled from Okta and disabling text input type: BOOLEAN_FLAG_TYPE @@ -89,6 +97,14 @@ flags: - segment: key: mil-app value: true + - key: ppm_spr + name: Enable PPM Small Package Reimbursement flag + type: BOOLEAN_FLAG_TYPE + enabled: false + rollouts: + - segment: + key: mil-app + value: false - key: nts name: NTS feature flag type: BOOLEAN_FLAG_TYPE diff --git a/migrations/app/ddl_migrations/ddl_tables/20250224200700_tbl_ppm_shipments.up.sql b/migrations/app/ddl_migrations/ddl_tables/20250224200700_tbl_ppm_shipments.up.sql new file mode 100644 index 00000000000..a23f7e68968 --- /dev/null +++ b/migrations/app/ddl_migrations/ddl_tables/20250224200700_tbl_ppm_shipments.up.sql @@ -0,0 +1,3 @@ +-- B-22653 Daniel Jordan add ppm_type column to ppm_shipments +ALTER TABLE ppm_shipments +ADD COLUMN IF NOT EXISTS ppm_type ppm_shipment_type NOT NULL DEFAULT 'INCENTIVE_BASED'; diff --git a/migrations/app/ddl_migrations/ddl_types/20250224202726_ty_ppm_shipment_type.up.sql b/migrations/app/ddl_migrations/ddl_types/20250224202726_ty_ppm_shipment_type.up.sql new file mode 100644 index 00000000000..8d06f35dec6 --- /dev/null +++ b/migrations/app/ddl_migrations/ddl_types/20250224202726_ty_ppm_shipment_type.up.sql @@ -0,0 +1,11 @@ +-- B-22653 Daniel Jordan add ppm_shipment_type +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'ppm_shipment_type') THEN + CREATE TYPE ppm_shipment_type AS ENUM ( + 'INCENTIVE_BASED', + 'ACTUAL_EXPENSE', + 'SMALL_PACKAGE' + ); + END IF; +END $$; diff --git a/migrations/app/ddl_migrations/ddl_types/20250224202738_ty_moving_expenses_type.up.sql b/migrations/app/ddl_migrations/ddl_types/20250224202738_ty_moving_expenses_type.up.sql new file mode 100644 index 00000000000..2cad6fed1f4 --- /dev/null +++ b/migrations/app/ddl_migrations/ddl_types/20250224202738_ty_moving_expenses_type.up.sql @@ -0,0 +1,2 @@ +-- B-22653 Daniel Jordan update moving_expense_type to include SMALL_PACKAGE +ALTER TYPE moving_expense_type ADD VALUE IF NOT EXISTS 'SMALL_PACKAGE'; \ No newline at end of file diff --git a/migrations/app/ddl_tables_manifest.txt b/migrations/app/ddl_tables_manifest.txt index 8fd6841c337..9de3cbf4e7e 100644 --- a/migrations/app/ddl_tables_manifest.txt +++ b/migrations/app/ddl_tables_manifest.txt @@ -1,3 +1,4 @@ # This is the tables migrations manifest. # If a migration is not recorded here, then it will error. # Naming convention: tbl_some_table.up.sql running will create this file. +20250224200700_tbl_ppm_shipments.up.sql diff --git a/migrations/app/ddl_types_manifest.txt b/migrations/app/ddl_types_manifest.txt index 9229c96f599..6faaa8a3f24 100644 --- a/migrations/app/ddl_types_manifest.txt +++ b/migrations/app/ddl_types_manifest.txt @@ -1,3 +1,5 @@ # This is the types migrations manifest. # If a migration is not recorded here, then it will error. # Naming convention: ty_some_type.up.sql running will create this file. +20250224202726_ty_ppm_shipment_type.up.sql +20250224202738_ty_moving_expenses_type.up.sql diff --git a/pkg/factory/ppm_shipment_factory.go b/pkg/factory/ppm_shipment_factory.go index cc87032285e..dc0cbe809c6 100644 --- a/pkg/factory/ppm_shipment_factory.go +++ b/pkg/factory/ppm_shipment_factory.go @@ -57,6 +57,7 @@ func buildPPMShipmentWithBuildType(db *pop.Connection, customs []Customization, } ppmShipment := models.PPMShipment{ + PPMType: models.PPMType(models.PPMTypeIncentiveBased), ShipmentID: shipment.ID, Shipment: shipment, Status: models.PPMShipmentStatusDraft, diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go index 3d41db0769c..3f9bea981ff 100644 --- a/pkg/gen/ghcapi/embedded_spec.go +++ b/pkg/gen/ghcapi/embedded_spec.go @@ -12320,6 +12320,9 @@ func init() { "pickupAddress": { "$ref": "#/definitions/Address" }, + "ppmType": { + "$ref": "#/definitions/PPMType" + }, "proGearWeight": { "description": "The estimated weight of the pro-gear being moved belonging to the service member.", "type": "integer", @@ -12534,6 +12537,17 @@ func init() { "COMPLETED" ] }, + "PPMType": { + "description": "Defines a PPM type", + "type": "string", + "title": "PPM Type", + "enum": [ + "INCENTIVE_BASED", + "ACTUAL_EXPENSE", + "SMALL_PACKAGE" + ], + "readOnly": true + }, "PWSViolation": { "description": "A PWS violation for an evaluation report", "type": "object", @@ -29916,6 +29930,9 @@ func init() { "pickupAddress": { "$ref": "#/definitions/Address" }, + "ppmType": { + "$ref": "#/definitions/PPMType" + }, "proGearWeight": { "description": "The estimated weight of the pro-gear being moved belonging to the service member.", "type": "integer", @@ -30130,6 +30147,17 @@ func init() { "COMPLETED" ] }, + "PPMType": { + "description": "Defines a PPM type", + "type": "string", + "title": "PPM Type", + "enum": [ + "INCENTIVE_BASED", + "ACTUAL_EXPENSE", + "SMALL_PACKAGE" + ], + "readOnly": true + }, "PWSViolation": { "description": "A PWS violation for an evaluation report", "type": "object", diff --git a/pkg/gen/ghcmessages/p_p_m_shipment.go b/pkg/gen/ghcmessages/p_p_m_shipment.go index 3b563ef0515..2b1228b27cf 100644 --- a/pkg/gen/ghcmessages/p_p_m_shipment.go +++ b/pkg/gen/ghcmessages/p_p_m_shipment.go @@ -136,6 +136,9 @@ type PPMShipment struct { // pickup address PickupAddress *Address `json:"pickupAddress,omitempty"` + // ppm type + PpmType PPMType `json:"ppmType,omitempty"` + // The estimated weight of the pro-gear being moved belonging to the service member. ProGearWeight *int64 `json:"proGearWeight"` @@ -272,6 +275,10 @@ func (m *PPMShipment) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validatePpmType(formats); err != nil { + res = append(res, err) + } + if err := m.validateProGearWeightTickets(formats); err != nil { res = append(res, err) } @@ -537,6 +544,23 @@ func (m *PPMShipment) validatePickupAddress(formats strfmt.Registry) error { return nil } +func (m *PPMShipment) validatePpmType(formats strfmt.Registry) error { + if swag.IsZero(m.PpmType) { // not required + return nil + } + + if err := m.PpmType.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("ppmType") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("ppmType") + } + return err + } + + return nil +} + func (m *PPMShipment) validateProGearWeightTickets(formats strfmt.Registry) error { if swag.IsZero(m.ProGearWeightTickets) { // not required return nil @@ -849,6 +873,10 @@ func (m *PPMShipment) ContextValidate(ctx context.Context, formats strfmt.Regist res = append(res, err) } + if err := m.contextValidatePpmType(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateProGearWeightTickets(ctx, formats); err != nil { res = append(res, err) } @@ -1027,6 +1055,24 @@ func (m *PPMShipment) contextValidatePickupAddress(ctx context.Context, formats return nil } +func (m *PPMShipment) contextValidatePpmType(ctx context.Context, formats strfmt.Registry) error { + + if swag.IsZero(m.PpmType) { // not required + return nil + } + + if err := m.PpmType.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("ppmType") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("ppmType") + } + return err + } + + return nil +} + func (m *PPMShipment) contextValidateProGearWeightTickets(ctx context.Context, formats strfmt.Registry) error { for i := 0; i < len(m.ProGearWeightTickets); i++ { diff --git a/pkg/gen/ghcmessages/p_p_m_type.go b/pkg/gen/ghcmessages/p_p_m_type.go new file mode 100644 index 00000000000..eb16d8cdaf2 --- /dev/null +++ b/pkg/gen/ghcmessages/p_p_m_type.go @@ -0,0 +1,92 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ghcmessages + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" +) + +// PPMType PPM Type +// +// # Defines a PPM type +// +// swagger:model PPMType +type PPMType string + +func NewPPMType(value PPMType) *PPMType { + return &value +} + +// Pointer returns a pointer to a freshly-allocated PPMType. +func (m PPMType) Pointer() *PPMType { + return &m +} + +const ( + + // PPMTypeINCENTIVEBASED captures enum value "INCENTIVE_BASED" + PPMTypeINCENTIVEBASED PPMType = "INCENTIVE_BASED" + + // PPMTypeACTUALEXPENSE captures enum value "ACTUAL_EXPENSE" + PPMTypeACTUALEXPENSE PPMType = "ACTUAL_EXPENSE" + + // PPMTypeSMALLPACKAGE captures enum value "SMALL_PACKAGE" + PPMTypeSMALLPACKAGE PPMType = "SMALL_PACKAGE" +) + +// for schema +var pPMTypeEnum []interface{} + +func init() { + var res []PPMType + if err := json.Unmarshal([]byte(`["INCENTIVE_BASED","ACTUAL_EXPENSE","SMALL_PACKAGE"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + pPMTypeEnum = append(pPMTypeEnum, v) + } +} + +func (m PPMType) validatePPMTypeEnum(path, location string, value PPMType) error { + if err := validate.EnumCase(path, location, value, pPMTypeEnum, true); err != nil { + return err + } + return nil +} + +// Validate validates this p p m type +func (m PPMType) Validate(formats strfmt.Registry) error { + var res []error + + // value enum + if err := m.validatePPMTypeEnum("", "body", m); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this p p m type based on the context it is used +func (m PPMType) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := validate.ReadOnly(ctx, "", "body", PPMType(m)); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/gen/internalapi/embedded_spec.go b/pkg/gen/internalapi/embedded_spec.go index 35d92886247..7fe513dece1 100644 --- a/pkg/gen/internalapi/embedded_spec.go +++ b/pkg/gen/internalapi/embedded_spec.go @@ -6511,6 +6511,9 @@ func init() { "pickupAddress": { "$ref": "#/definitions/Address" }, + "ppmType": { + "$ref": "#/definitions/PPMType" + }, "proGearWeight": { "description": "The estimated weight of the pro-gear being moved belonging to the service member.", "type": "integer", @@ -6684,6 +6687,17 @@ func init() { ], "readOnly": true }, + "PPMType": { + "description": "Defines a PPM type", + "type": "string", + "title": "PPM Type", + "enum": [ + "INCENTIVE_BASED", + "ACTUAL_EXPENSE", + "SMALL_PACKAGE" + ], + "readOnly": true + }, "PatchMovePayload": { "type": "object", "required": [ @@ -15653,6 +15667,9 @@ func init() { "pickupAddress": { "$ref": "#/definitions/Address" }, + "ppmType": { + "$ref": "#/definitions/PPMType" + }, "proGearWeight": { "description": "The estimated weight of the pro-gear being moved belonging to the service member.", "type": "integer", @@ -15826,6 +15843,17 @@ func init() { ], "readOnly": true }, + "PPMType": { + "description": "Defines a PPM type", + "type": "string", + "title": "PPM Type", + "enum": [ + "INCENTIVE_BASED", + "ACTUAL_EXPENSE", + "SMALL_PACKAGE" + ], + "readOnly": true + }, "PatchMovePayload": { "type": "object", "required": [ diff --git a/pkg/gen/internalmessages/p_p_m_shipment.go b/pkg/gen/internalmessages/p_p_m_shipment.go index 43181ef2904..294a0608532 100644 --- a/pkg/gen/internalmessages/p_p_m_shipment.go +++ b/pkg/gen/internalmessages/p_p_m_shipment.go @@ -136,6 +136,9 @@ type PPMShipment struct { // pickup address PickupAddress *Address `json:"pickupAddress,omitempty"` + // ppm type + PpmType PPMType `json:"ppmType,omitempty"` + // The estimated weight of the pro-gear being moved belonging to the service member. ProGearWeight *int64 `json:"proGearWeight"` @@ -272,6 +275,10 @@ func (m *PPMShipment) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validatePpmType(formats); err != nil { + res = append(res, err) + } + if err := m.validateProGearWeightTickets(formats); err != nil { res = append(res, err) } @@ -537,6 +544,23 @@ func (m *PPMShipment) validatePickupAddress(formats strfmt.Registry) error { return nil } +func (m *PPMShipment) validatePpmType(formats strfmt.Registry) error { + if swag.IsZero(m.PpmType) { // not required + return nil + } + + if err := m.PpmType.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("ppmType") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("ppmType") + } + return err + } + + return nil +} + func (m *PPMShipment) validateProGearWeightTickets(formats strfmt.Registry) error { if swag.IsZero(m.ProGearWeightTickets) { // not required return nil @@ -849,6 +873,10 @@ func (m *PPMShipment) ContextValidate(ctx context.Context, formats strfmt.Regist res = append(res, err) } + if err := m.contextValidatePpmType(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateProGearWeightTickets(ctx, formats); err != nil { res = append(res, err) } @@ -1027,6 +1055,24 @@ func (m *PPMShipment) contextValidatePickupAddress(ctx context.Context, formats return nil } +func (m *PPMShipment) contextValidatePpmType(ctx context.Context, formats strfmt.Registry) error { + + if swag.IsZero(m.PpmType) { // not required + return nil + } + + if err := m.PpmType.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("ppmType") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("ppmType") + } + return err + } + + return nil +} + func (m *PPMShipment) contextValidateProGearWeightTickets(ctx context.Context, formats strfmt.Registry) error { for i := 0; i < len(m.ProGearWeightTickets); i++ { diff --git a/pkg/gen/internalmessages/p_p_m_type.go b/pkg/gen/internalmessages/p_p_m_type.go new file mode 100644 index 00000000000..4ba102b4a09 --- /dev/null +++ b/pkg/gen/internalmessages/p_p_m_type.go @@ -0,0 +1,92 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package internalmessages + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" +) + +// PPMType PPM Type +// +// # Defines a PPM type +// +// swagger:model PPMType +type PPMType string + +func NewPPMType(value PPMType) *PPMType { + return &value +} + +// Pointer returns a pointer to a freshly-allocated PPMType. +func (m PPMType) Pointer() *PPMType { + return &m +} + +const ( + + // PPMTypeINCENTIVEBASED captures enum value "INCENTIVE_BASED" + PPMTypeINCENTIVEBASED PPMType = "INCENTIVE_BASED" + + // PPMTypeACTUALEXPENSE captures enum value "ACTUAL_EXPENSE" + PPMTypeACTUALEXPENSE PPMType = "ACTUAL_EXPENSE" + + // PPMTypeSMALLPACKAGE captures enum value "SMALL_PACKAGE" + PPMTypeSMALLPACKAGE PPMType = "SMALL_PACKAGE" +) + +// for schema +var pPMTypeEnum []interface{} + +func init() { + var res []PPMType + if err := json.Unmarshal([]byte(`["INCENTIVE_BASED","ACTUAL_EXPENSE","SMALL_PACKAGE"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + pPMTypeEnum = append(pPMTypeEnum, v) + } +} + +func (m PPMType) validatePPMTypeEnum(path, location string, value PPMType) error { + if err := validate.EnumCase(path, location, value, pPMTypeEnum, true); err != nil { + return err + } + return nil +} + +// Validate validates this p p m type +func (m PPMType) Validate(formats strfmt.Registry) error { + var res []error + + // value enum + if err := m.validatePPMTypeEnum("", "body", m); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this p p m type based on the context it is used +func (m PPMType) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := validate.ReadOnly(ctx, "", "body", PPMType(m)); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/models/moving_expense.go b/pkg/models/moving_expense.go index 97ea06f1a38..92b4d5ab77c 100644 --- a/pkg/models/moving_expense.go +++ b/pkg/models/moving_expense.go @@ -29,6 +29,8 @@ const ( MovingExpenseReceiptTypeTolls MovingExpenseReceiptType = "TOLLS" // MovingExpenseReceiptTypeWeighingFee captures enum value "WEIGHING_FEE" MovingExpenseReceiptTypeWeighingFee MovingExpenseReceiptType = "WEIGHING_FEE" + // MovingExpenseReceiptTypeSmallPackage captures enum value "SMALL_PACKAGE" + MovingExpenseReceiptTypeSmallPackage MovingExpenseReceiptType = "SMALL_PACKAGE" // MovingExpenseReceiptTypeOther captures enum value "OTHER" MovingExpenseReceiptTypeOther MovingExpenseReceiptType = "OTHER" ) diff --git a/pkg/models/ppm_shipment.go b/pkg/models/ppm_shipment.go index d53bdb5b3ac..392a3fa9562 100644 --- a/pkg/models/ppm_shipment.go +++ b/pkg/models/ppm_shipment.go @@ -180,9 +180,22 @@ type PPMDocuments struct { ProgearWeightTickets } +// PPMType represents the type of a PPM shipment +type PPMType string + +const ( + // PPMTypeIncentiveBased captures enum value "INCENTIVE_BASED" + PPMTypeIncentiveBased PPMType = "INCENTIVE_BASED" + // PPMTypeActualExpense captures enum value "ACTUAL_EXPENSE" + PPMTypeActualExpense PPMType = "ACTUAL_EXPENSE" + // PPMTypeSmallPackage captures enum value "SMALL_PACKAGE" + PPMTypeSmallPackage PPMType = "SMALL_PACKAGE" +) + // PPMShipment is the portion of a move that a service member performs themselves type PPMShipment struct { ID uuid.UUID `json:"id" db:"id"` + PPMType PPMType `json:"ppm_type" db:"ppm_type"` ShipmentID uuid.UUID `json:"shipment_id" db:"shipment_id"` Shipment MTOShipment `belongs_to:"mto_shipments" fk_id:"shipment_id"` CreatedAt time.Time `json:"created_at" db:"created_at"` diff --git a/pkg/services/ppmshipment/ppm_shipment_creator.go b/pkg/services/ppmshipment/ppm_shipment_creator.go index 75f8e50f7ae..b76cfbe778d 100644 --- a/pkg/services/ppmshipment/ppm_shipment_creator.go +++ b/pkg/services/ppmshipment/ppm_shipment_creator.go @@ -56,6 +56,11 @@ func (f *ppmShipmentCreator) createPPMShipment(appCtx appcontext.AppContext, ppm return apperror.NewInvalidInputError(uuid.Nil, nil, nil, "Must have a DRAFT or SUBMITTED status associated with PPM shipment") } + // default PPM type is incentive based + if ppmShipment.PPMType == "" { + ppmShipment.PPMType = models.PPMType(models.PPMTypeIncentiveBased) + } + // create pickup and destination addresses if ppmShipment.PickupAddress != nil { address, err = f.addressCreator.CreateAddress(txnAppCtx, ppmShipment.PickupAddress) diff --git a/pkg/services/ppmshipment/ppm_shipment_creator_test.go b/pkg/services/ppmshipment/ppm_shipment_creator_test.go index 22fc576d525..73c545ca608 100644 --- a/pkg/services/ppmshipment/ppm_shipment_creator_test.go +++ b/pkg/services/ppmshipment/ppm_shipment_creator_test.go @@ -108,6 +108,7 @@ func (suite *PPMShipmentSuite) TestPPMShipmentCreator() { suite.Nil(err) suite.NotNil(createdPPMShipment) + suite.Equal(createdPPMShipment.PPMType, models.PPMTypeIncentiveBased) suite.Equal(createdPPMShipment.Shipment.MarketCode, models.MarketCodeDomestic) }) diff --git a/src/shared/constants.js b/src/shared/constants.js index 56b7601c585..0073ee5e28f 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -102,6 +102,18 @@ export const SHIPMENT_TYPES = { UNACCOMPANIED_BAGGAGE: 'UNACCOMPANIED_BAGGAGE', }; +export const PPM_TYPES = { + INCENTIVE_BASED: 'INCENTIVE_BASED', + ACTUAL_EXPENSE: 'ACTUAL_EXPENSE', + SMALL_PACKAGE: 'SMALL_PACKAGE', +}; + +export const ppmTypeLabels = [ + { key: PPM_TYPES.INCENTIVE_BASED, label: 'Incentive-based' }, + { key: PPM_TYPES.ACTUAL_EXPENSE, label: 'Actual Expense' }, + { key: PPM_TYPES.SMALL_PACKAGE, label: 'Small Package' }, +]; + // These constants are used for forming URLs that have the shipment type in // them so that they are human readable. export const SHIPMENT_OPTIONS_URL = { @@ -203,6 +215,7 @@ export const DEFAULT_EMPTY_VALUE = '—'; // emdash export const FEATURE_FLAG_KEYS = { PPM: 'ppm', + PPM_SPR: 'ppm_spr', NTS: 'nts', NTSR: 'ntsr', BOAT: 'boat', diff --git a/swagger-def/definitions/PPMShipment.yaml b/swagger-def/definitions/PPMShipment.yaml index c75524f94eb..274ec216a0b 100644 --- a/swagger-def/definitions/PPMShipment.yaml +++ b/swagger-def/definitions/PPMShipment.yaml @@ -7,6 +7,8 @@ properties: format: uuid type: string readOnly: true + ppmType: + $ref: 'PPMType.yaml' shipmentId: description: The id of the parent MTOShipment object example: 1f2270c7-7166-40ae-981e-b200ebdf3054 diff --git a/swagger-def/definitions/PPMType.yaml b/swagger-def/definitions/PPMType.yaml new file mode 100644 index 00000000000..a87dc0c57ee --- /dev/null +++ b/swagger-def/definitions/PPMType.yaml @@ -0,0 +1,8 @@ +type: string +title: PPM Type +description: Defines a PPM type +readOnly: true +enum: + - INCENTIVE_BASED + - ACTUAL_EXPENSE + - SMALL_PACKAGE diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml index e51d85a9794..b9f0c6d6e06 100644 --- a/swagger/ghc.yaml +++ b/swagger/ghc.yaml @@ -9715,6 +9715,15 @@ definitions: given SIT service items for an instance of SIT (Either Origin or Destination), grouped by the date they went into SIT and service items limited explicitly to SIT related Re Service Codes. + PPMType: + type: string + title: PPM Type + description: Defines a PPM type + readOnly: true + enum: + - INCENTIVE_BASED + - ACTUAL_EXPENSE + - SMALL_PACKAGE PPMShipmentStatus: description: | Status of the PPM Shipment: @@ -10340,6 +10349,8 @@ definitions: format: uuid type: string readOnly: true + ppmType: + $ref: '#/definitions/PPMType' shipmentId: description: The id of the parent MTOShipment object example: 1f2270c7-7166-40ae-981e-b200ebdf3054 diff --git a/swagger/internal.yaml b/swagger/internal.yaml index 2f772c2ffcc..f9ded41f350 100644 --- a/swagger/internal.yaml +++ b/swagger/internal.yaml @@ -2967,6 +2967,15 @@ definitions: - id - service_member_id - uploads + PPMType: + type: string + title: PPM Type + description: Defines a PPM type + readOnly: true + enum: + - INCENTIVE_BASED + - ACTUAL_EXPENSE + - SMALL_PACKAGE PPMShipmentStatus: description: | Status of the PPM Shipment: @@ -3598,6 +3607,8 @@ definitions: format: uuid type: string readOnly: true + ppmType: + $ref: '#/definitions/PPMType' shipmentId: description: The id of the parent MTOShipment object example: 1f2270c7-7166-40ae-981e-b200ebdf3054 From 7cc7f9d1f5fbbb8d95742f0c1ab18c07e82a4b2c Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Tue, 25 Feb 2025 16:18:36 +0000 Subject: [PATCH 23/26] updating tests to use factories, some comment verbiage --- pkg/models/transportation_office.go | 2 +- pkg/models/transportation_office_test.go | 70 +++++++++++++------ .../transportation_office_fetcher_test.go | 69 ++++++++++++------ 3 files changed, 97 insertions(+), 44 deletions(-) diff --git a/pkg/models/transportation_office.go b/pkg/models/transportation_office.go index 21a53cc621f..5490da6c743 100644 --- a/pkg/models/transportation_office.go +++ b/pkg/models/transportation_office.go @@ -71,7 +71,7 @@ func FetchNearestTransportationOffice(tx *pop.Connection, long float32, lat floa return to, nil } -// GetCounselingOffices is a db function that returns all the transportation offices in the GBLOC +// GetCounselingOffices calls a db function that returns all the transportation offices in the GBLOC // of the given duty location where provides_services_counseling = true func GetCounselingOffices(db *pop.Connection, dutyLocationID uuid.UUID, serviceMemberID uuid.UUID) (TransportationOffices, error) { var officeList TransportationOffices diff --git a/pkg/models/transportation_office_test.go b/pkg/models/transportation_office_test.go index dc16211d3d8..32fc2e09cdb 100644 --- a/pkg/models/transportation_office_test.go +++ b/pkg/models/transportation_office_test.go @@ -10,8 +10,6 @@ package models_test import ( - "github.com/gofrs/uuid" - "github.com/transcom/mymove/pkg/factory" m "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services/address" @@ -83,12 +81,14 @@ func (suite *ModelSuite) Test_TransportationOffice() { } func (suite *ModelSuite) TestGetCounselingOffices() { - customAddress1 := m.Address{ - ID: uuid.Must(uuid.NewV4()), - PostalCode: "59801", - } + customAddress1 := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: m.Address{ + PostalCode: "59801", + }, + }, + }, nil) factory.BuildDutyLocation(suite.DB(), []factory.Customization{ - {Model: customAddress1, Type: &factory.Addresses.DutyLocationAddress}, { Model: m.DutyLocation{ ProvidesServicesCounseling: false, @@ -99,15 +99,22 @@ func (suite *ModelSuite) TestGetCounselingOffices() { Name: "PPPO Holloman AFB - USAF", }, }, + { + Model: customAddress1, + LinkOnly: true, + Type: &factory.Addresses.DutyLocationAddress, + }, }, nil) // duty locations in KKFA with provides_services_counseling = true - customAddress2 := m.Address{ - ID: uuid.Must(uuid.NewV4()), - PostalCode: "59801", - } + customAddress2 := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: m.Address{ + PostalCode: "59801", + }, + }, + }, nil) factory.BuildDutyLocation(suite.DB(), []factory.Customization{ - {Model: customAddress2, Type: &factory.Addresses.DutyLocationAddress}, { Model: m.DutyLocation{ ProvidesServicesCounseling: true, @@ -118,14 +125,21 @@ func (suite *ModelSuite) TestGetCounselingOffices() { Name: "PPPO Hill AFB - USAF", }, }, + { + Model: customAddress2, + LinkOnly: true, + Type: &factory.Addresses.DutyLocationAddress, + }, }, nil) - customAddress3 := m.Address{ - ID: uuid.Must(uuid.NewV4()), - PostalCode: "59801", - } + customAddress3 := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: m.Address{ + PostalCode: "59801", + }, + }, + }, nil) origDutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ - {Model: customAddress3, Type: &factory.Addresses.DutyLocationAddress}, { Model: m.DutyLocation{ ProvidesServicesCounseling: true, @@ -138,15 +152,22 @@ func (suite *ModelSuite) TestGetCounselingOffices() { ProvidesCloseout: true, }, }, + { + Model: customAddress3, + LinkOnly: true, + Type: &factory.Addresses.DutyLocationAddress, + }, }, nil) // this one will not show in the return since it is not KKFA - customAddress4 := m.Address{ - ID: uuid.Must(uuid.NewV4()), - PostalCode: "20906", - } + customAddress4 := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: m.Address{ + PostalCode: "20906", + }, + }, + }, nil) factory.BuildDutyLocation(suite.DB(), []factory.Customization{ - {Model: customAddress4, Type: &factory.Addresses.DutyLocationAddress}, { Model: m.DutyLocation{ ProvidesServicesCounseling: true, @@ -159,6 +180,11 @@ func (suite *ModelSuite) TestGetCounselingOffices() { ProvidesCloseout: true, }, }, + { + Model: customAddress4, + LinkOnly: true, + Type: &factory.Addresses.DutyLocationAddress, + }, }, nil) armyAffliation := m.AffiliationARMY diff --git a/pkg/services/transportation_office/transportation_office_fetcher_test.go b/pkg/services/transportation_office/transportation_office_fetcher_test.go index fdf7606860c..6d5134b2c8f 100644 --- a/pkg/services/transportation_office/transportation_office_fetcher_test.go +++ b/pkg/services/transportation_office/transportation_office_fetcher_test.go @@ -101,13 +101,14 @@ func (suite *TransportationOfficeServiceSuite) Test_SortedTransportationOffices( func (suite *TransportationOfficeServiceSuite) Test_FindCounselingOffices() { suite.toFetcher = NewTransportationOfficesFetcher() - // duty location in KKFA with provides_services_counseling = false - customAddress1 := models.Address{ - ID: uuid.Must(uuid.NewV4()), - PostalCode: "59801", - } + customAddress1 := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + PostalCode: "59801", + }, + }, + }, nil) factory.BuildDutyLocation(suite.DB(), []factory.Customization{ - {Model: customAddress1, Type: &factory.Addresses.DutyLocationAddress}, { Model: models.DutyLocation{ ProvidesServicesCounseling: false, @@ -118,15 +119,22 @@ func (suite *TransportationOfficeServiceSuite) Test_FindCounselingOffices() { Name: "PPPO Holloman AFB - USAF", }, }, + { + Model: customAddress1, + LinkOnly: true, + Type: &factory.Addresses.DutyLocationAddress, + }, }, nil) // duty locations in KKFA with provides_services_counseling = true - customAddress2 := models.Address{ - ID: uuid.Must(uuid.NewV4()), - PostalCode: "59801", - } + customAddress2 := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + PostalCode: "59801", + }, + }, + }, nil) factory.BuildDutyLocation(suite.DB(), []factory.Customization{ - {Model: customAddress2, Type: &factory.Addresses.DutyLocationAddress}, { Model: models.DutyLocation{ ProvidesServicesCounseling: true, @@ -137,14 +145,21 @@ func (suite *TransportationOfficeServiceSuite) Test_FindCounselingOffices() { Name: "PPPO Hill AFB - USAF", }, }, + { + Model: customAddress2, + LinkOnly: true, + Type: &factory.Addresses.DutyLocationAddress, + }, }, nil) - customAddress3 := models.Address{ - ID: uuid.Must(uuid.NewV4()), - PostalCode: "59801", - } + customAddress3 := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + PostalCode: "59801", + }, + }, + }, nil) origDutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ - {Model: customAddress3, Type: &factory.Addresses.DutyLocationAddress}, { Model: models.DutyLocation{ ProvidesServicesCounseling: true, @@ -157,15 +172,22 @@ func (suite *TransportationOfficeServiceSuite) Test_FindCounselingOffices() { ProvidesCloseout: true, }, }, + { + Model: customAddress3, + LinkOnly: true, + Type: &factory.Addresses.DutyLocationAddress, + }, }, nil) // this one will not show in the return since it is not KKFA - customAddress4 := models.Address{ - ID: uuid.Must(uuid.NewV4()), - PostalCode: "20906", - } + customAddress4 := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + PostalCode: "20906", + }, + }, + }, nil) factory.BuildDutyLocation(suite.DB(), []factory.Customization{ - {Model: customAddress4, Type: &factory.Addresses.DutyLocationAddress}, { Model: models.DutyLocation{ ProvidesServicesCounseling: true, @@ -178,6 +200,11 @@ func (suite *TransportationOfficeServiceSuite) Test_FindCounselingOffices() { ProvidesCloseout: true, }, }, + { + Model: customAddress4, + LinkOnly: true, + Type: &factory.Addresses.DutyLocationAddress, + }, }, nil) armyAffliation := models.AffiliationARMY From 00c290a610651ae6d30a873c872d504613713261 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Tue, 25 Feb 2025 17:43:42 +0000 Subject: [PATCH 24/26] adding some suhweet validation --- pkg/models/ppm_shipment.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/models/ppm_shipment.go b/pkg/models/ppm_shipment.go index 392a3fa9562..ecb04cc45ca 100644 --- a/pkg/models/ppm_shipment.go +++ b/pkg/models/ppm_shipment.go @@ -192,6 +192,13 @@ const ( PPMTypeSmallPackage PPMType = "SMALL_PACKAGE" ) +// AllowedPPMTypes is a list of all the allowed values for PPM types +var AllowedPPMTypes = []string{ + string(PPMTypeIncentiveBased), + string(PPMTypeActualExpense), + string(PPMTypeSmallPackage), +} + // PPMShipment is the portion of a move that a service member performs themselves type PPMShipment struct { ID uuid.UUID `json:"id" db:"id"` @@ -277,6 +284,7 @@ type PPMShipments []PPMShipment func (p PPMShipment) Validate(_ *pop.Connection) (*validate.Errors, error) { return validate.Validate( &validators.UUIDIsPresent{Name: "ShipmentID", Field: p.ShipmentID}, + &validators.StringInclusion{Name: "PPMType", Field: string(p.PPMType), List: AllowedPPMTypes}, &OptionalTimeIsPresent{Name: "DeletedAt", Field: p.DeletedAt}, &validators.TimeIsPresent{Name: "ExpectedDepartureDate", Field: p.ExpectedDepartureDate}, &validators.StringInclusion{Name: "Status", Field: string(p.Status), List: AllowedPPMShipmentStatuses}, From 109584189d04b647018a43a11b802044b88dbad0 Mon Sep 17 00:00:00 2001 From: Alex Lusk Date: Tue, 25 Feb 2025 18:31:30 +0000 Subject: [PATCH 25/26] Include all rate area info in prime api for only OCONUS *shipments* --- .../primeapiv3/payloads/model_to_payload.go | 15 +++-- .../payloads/model_to_payload_test.go | 23 +++++-- .../mto_shipment_rate_area_fetcher.go | 59 +++++++++--------- .../mto_shipment_rate_area_fetcher_test.go | 62 +++++++++---------- 4 files changed, 90 insertions(+), 69 deletions(-) diff --git a/pkg/handlers/primeapiv3/payloads/model_to_payload.go b/pkg/handlers/primeapiv3/payloads/model_to_payload.go index aba7a9c1718..0cb7b93e366 100644 --- a/pkg/handlers/primeapiv3/payloads/model_to_payload.go +++ b/pkg/handlers/primeapiv3/payloads/model_to_payload.go @@ -93,12 +93,15 @@ func MoveTaskOrderWithShipmentRateAreas(appCtx appcontext.AppContext, moveTaskOr } // Origin/Destination RateArea will be present on root shipment level for all non-PPM shipment types for _, shipment := range payload.MtoShipments { - if shipment.PpmShipment != nil { - shipment.PpmShipment.OriginRateArea = PostalCodeToRateArea(shipment.PpmShipment.PickupAddress.PostalCode, shipmentPostalCodeRateAreaLookupMap) - shipment.PpmShipment.DestinationRateArea = PostalCodeToRateArea(shipment.PpmShipment.DestinationAddress.PostalCode, shipmentPostalCodeRateAreaLookupMap) - } else { - shipment.OriginRateArea = PostalCodeToRateArea(shipment.PickupAddress.PostalCode, shipmentPostalCodeRateAreaLookupMap) - shipment.DestinationRateArea = PostalCodeToRateArea(shipment.DestinationAddress.PostalCode, shipmentPostalCodeRateAreaLookupMap) + // B-22767: We want both domestic and international rate area info but only for international shipments + if shipment.MarketCode == string(models.MarketCodeInternational) { + if shipment.PpmShipment != nil { + shipment.PpmShipment.OriginRateArea = PostalCodeToRateArea(shipment.PpmShipment.PickupAddress.PostalCode, shipmentPostalCodeRateAreaLookupMap) + shipment.PpmShipment.DestinationRateArea = PostalCodeToRateArea(shipment.PpmShipment.DestinationAddress.PostalCode, shipmentPostalCodeRateAreaLookupMap) + } else { + shipment.OriginRateArea = PostalCodeToRateArea(shipment.PickupAddress.PostalCode, shipmentPostalCodeRateAreaLookupMap) + shipment.DestinationRateArea = PostalCodeToRateArea(shipment.DestinationAddress.PostalCode, shipmentPostalCodeRateAreaLookupMap) + } } } } diff --git a/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go b/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go index f0f7036eac6..95529b800c6 100644 --- a/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go +++ b/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go @@ -151,11 +151,13 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { const fairbanksAlaskaPostalCode = "99716" const anchorageAlaskaPostalCode = "99521" const wasillaAlaskaPostalCode = "99652" + const beverlyHillsCAPostalCode = "90210" //clear MTOShipment and rebuild with specifics for test newMove.MTOShipments = newMove.MTOShipments[:0] newMove.MTOShipments = append(newMove.MTOShipments, models.MTOShipment{ + MarketCode: models.MarketCodeInternational, PickupAddress: &models.Address{ StreetAddress1: "123 Main St", StreetAddress2: &streetAddress2, @@ -175,6 +177,7 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { }, }) newMove.MTOShipments = append(newMove.MTOShipments, models.MTOShipment{ + MarketCode: models.MarketCodeInternational, PickupAddress: &models.Address{ StreetAddress1: "123 Main St", StreetAddress2: &streetAddress2, @@ -195,6 +198,7 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { }) newMove.MTOShipments = append(newMove.MTOShipments, models.MTOShipment{ ShipmentType: models.MTOShipmentTypePPM, + MarketCode: models.MarketCodeInternational, PPMShipment: &models.PPMShipment{ ID: uuid.Must(uuid.NewV4()), ApprovedAt: models.TimePointer(time.Now()), @@ -224,6 +228,8 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { }, }) newMove.MTOShipments = append(newMove.MTOShipments, models.MTOShipment{ + ShipmentType: models.MTOShipmentTypePPM, + MarketCode: models.MarketCodeDomestic, PPMShipment: &models.PPMShipment{ ID: uuid.Must(uuid.NewV4()), ApprovedAt: models.TimePointer(time.Now()), @@ -240,7 +246,7 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { StreetAddress3: &streetAddress3, City: "Beverly Hills", State: "CA", - PostalCode: "90210", + PostalCode: beverlyHillsCAPostalCode, }, DestinationAddress: &models.Address{ StreetAddress1: "123 Main St", @@ -248,18 +254,19 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { StreetAddress3: &streetAddress3, City: "Beverly Hills", State: "CA", - PostalCode: "90210", + PostalCode: beverlyHillsCAPostalCode, }, }, }) newMove.MTOShipments = append(newMove.MTOShipments, models.MTOShipment{ + MarketCode: models.MarketCodeDomestic, PickupAddress: &models.Address{ StreetAddress1: "123 Main St", StreetAddress2: &streetAddress2, StreetAddress3: &streetAddress3, City: "Beverly Hills", State: "CA", - PostalCode: "90210", + PostalCode: beverlyHillsCAPostalCode, DestinationGbloc: models.StringPointer("JEAT"), }, DestinationAddress: &models.Address{ @@ -268,7 +275,7 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { StreetAddress3: &streetAddress3, City: "Beverly Hills", State: "CA", - PostalCode: "90210", + PostalCode: beverlyHillsCAPostalCode, DestinationGbloc: models.StringPointer("JEAT"), }, }) @@ -335,6 +342,14 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { Name: wasillaAlaskaPostalCode, }, }, + { + PostalCode: beverlyHillsCAPostalCode, + RateArea: &models.ReRateArea{ + ID: uuid.Must(uuid.NewV4()), + Code: beverlyHillsCAPostalCode, + Name: beverlyHillsCAPostalCode, + }, + }, } returnedModel = MoveTaskOrderWithShipmentRateAreas(suite.AppContextForTest(), newMove, &shipmentPostalCodeRateArea) diff --git a/pkg/services/mto_shipment/mto_shipment_rate_area_fetcher.go b/pkg/services/mto_shipment/mto_shipment_rate_area_fetcher.go index ed7381c60a9..de42e34918f 100644 --- a/pkg/services/mto_shipment/mto_shipment_rate_area_fetcher.go +++ b/pkg/services/mto_shipment/mto_shipment_rate_area_fetcher.go @@ -36,37 +36,40 @@ func (f mtoShipmentRateAreaFetcher) GetPrimeMoveShipmentRateAreas(appCtx appcont var oconusPostalCodes = make([]string, 0) var conusPostalCodes = make([]string, 0) for _, shipment := range moveTaskOrder.MTOShipments { - if shipment.PickupAddress != nil { - if !slices.Contains(oconusPostalCodes, shipment.PickupAddress.PostalCode) && - shipment.PickupAddress.IsOconus != nil && *shipment.PickupAddress.IsOconus { - oconusPostalCodes = append(oconusPostalCodes, shipment.PickupAddress.PostalCode) - } else if !slices.Contains(conusPostalCodes, shipment.PickupAddress.PostalCode) { - conusPostalCodes = append(conusPostalCodes, shipment.PickupAddress.PostalCode) - } - } - if shipment.DestinationAddress != nil { - if !slices.Contains(oconusPostalCodes, shipment.DestinationAddress.PostalCode) && - shipment.DestinationAddress.IsOconus != nil && *shipment.DestinationAddress.IsOconus { - oconusPostalCodes = append(oconusPostalCodes, shipment.DestinationAddress.PostalCode) - } else if !slices.Contains(conusPostalCodes, shipment.DestinationAddress.PostalCode) { - conusPostalCodes = append(conusPostalCodes, shipment.DestinationAddress.PostalCode) + // B-22767: We want both domestic and international rate area info but only for international shipments + if shipment.MarketCode == models.MarketCodeInternational { + if shipment.PickupAddress != nil { + if !slices.Contains(oconusPostalCodes, shipment.PickupAddress.PostalCode) && + shipment.PickupAddress.IsOconus != nil && *shipment.PickupAddress.IsOconus { + oconusPostalCodes = append(oconusPostalCodes, shipment.PickupAddress.PostalCode) + } else if !slices.Contains(conusPostalCodes, shipment.PickupAddress.PostalCode) { + conusPostalCodes = append(conusPostalCodes, shipment.PickupAddress.PostalCode) + } } - } - if shipment.PPMShipment != nil { - if shipment.PPMShipment.PickupAddress != nil { - if !slices.Contains(oconusPostalCodes, shipment.PPMShipment.PickupAddress.PostalCode) && - shipment.PPMShipment.PickupAddress.IsOconus != nil && *shipment.PPMShipment.PickupAddress.IsOconus { - oconusPostalCodes = append(oconusPostalCodes, shipment.PPMShipment.PickupAddress.PostalCode) - } else if !slices.Contains(conusPostalCodes, shipment.PPMShipment.PickupAddress.PostalCode) { - conusPostalCodes = append(conusPostalCodes, shipment.PPMShipment.PickupAddress.PostalCode) + if shipment.DestinationAddress != nil { + if !slices.Contains(oconusPostalCodes, shipment.DestinationAddress.PostalCode) && + shipment.DestinationAddress.IsOconus != nil && *shipment.DestinationAddress.IsOconus { + oconusPostalCodes = append(oconusPostalCodes, shipment.DestinationAddress.PostalCode) + } else if !slices.Contains(conusPostalCodes, shipment.DestinationAddress.PostalCode) { + conusPostalCodes = append(conusPostalCodes, shipment.DestinationAddress.PostalCode) } } - if shipment.PPMShipment.DestinationAddress != nil { - if !slices.Contains(oconusPostalCodes, shipment.PPMShipment.DestinationAddress.PostalCode) && - shipment.PPMShipment.DestinationAddress.IsOconus != nil && *shipment.PPMShipment.DestinationAddress.IsOconus { - oconusPostalCodes = append(oconusPostalCodes, shipment.PPMShipment.DestinationAddress.PostalCode) - } else if !slices.Contains(conusPostalCodes, shipment.PPMShipment.DestinationAddress.PostalCode) { - conusPostalCodes = append(conusPostalCodes, shipment.PPMShipment.DestinationAddress.PostalCode) + if shipment.PPMShipment != nil { + if shipment.PPMShipment.PickupAddress != nil { + if !slices.Contains(oconusPostalCodes, shipment.PPMShipment.PickupAddress.PostalCode) && + shipment.PPMShipment.PickupAddress.IsOconus != nil && *shipment.PPMShipment.PickupAddress.IsOconus { + oconusPostalCodes = append(oconusPostalCodes, shipment.PPMShipment.PickupAddress.PostalCode) + } else if !slices.Contains(conusPostalCodes, shipment.PPMShipment.PickupAddress.PostalCode) { + conusPostalCodes = append(conusPostalCodes, shipment.PPMShipment.PickupAddress.PostalCode) + } + } + if shipment.PPMShipment.DestinationAddress != nil { + if !slices.Contains(oconusPostalCodes, shipment.PPMShipment.DestinationAddress.PostalCode) && + shipment.PPMShipment.DestinationAddress.IsOconus != nil && *shipment.PPMShipment.DestinationAddress.IsOconus { + oconusPostalCodes = append(oconusPostalCodes, shipment.PPMShipment.DestinationAddress.PostalCode) + } else if !slices.Contains(conusPostalCodes, shipment.PPMShipment.DestinationAddress.PostalCode) { + conusPostalCodes = append(conusPostalCodes, shipment.PPMShipment.DestinationAddress.PostalCode) + } } } } diff --git a/pkg/services/mto_shipment/mto_shipment_rate_area_fetcher_test.go b/pkg/services/mto_shipment/mto_shipment_rate_area_fetcher_test.go index e94fea2c4a1..b594c3f3a6c 100644 --- a/pkg/services/mto_shipment/mto_shipment_rate_area_fetcher_test.go +++ b/pkg/services/mto_shipment/mto_shipment_rate_area_fetcher_test.go @@ -3,6 +3,7 @@ package mtoshipment import ( "database/sql" "fmt" + "slices" "time" "github.com/gofrs/uuid" @@ -46,6 +47,7 @@ func (suite *MTOShipmentServiceSuite) TestGetMoveShipmentRateArea() { PostalCode: anchorageAlaskaPostalCode, IsOconus: models.BoolPointer(true), }, + MarketCode: models.MarketCodeInternational, }, models.MTOShipment{ PickupAddress: &models.Address{ @@ -62,15 +64,16 @@ func (suite *MTOShipmentServiceSuite) TestGetMoveShipmentRateArea() { PostalCode: sanDiegoCAPostalCode, IsOconus: models.BoolPointer(false), }, + MarketCode: models.MarketCodeDomestic, }, models.MTOShipment{ PPMShipment: &models.PPMShipment{ PickupAddress: &models.Address{ StreetAddress1: "123 Main St", - City: "Wasilla", - State: "AK", - PostalCode: wasillaAlaskaPostalCode, - IsOconus: models.BoolPointer(true), + City: "Beverly Hills", + State: "CA", + PostalCode: beverlyHillsCAPostalCode, + IsOconus: models.BoolPointer(false), }, DestinationAddress: &models.Address{ StreetAddress1: "123 Main St", @@ -80,6 +83,7 @@ func (suite *MTOShipmentServiceSuite) TestGetMoveShipmentRateArea() { IsOconus: models.BoolPointer(true), }, }, + MarketCode: models.MarketCodeInternational, }, }, } @@ -214,9 +218,9 @@ func (suite *MTOShipmentServiceSuite) TestGetMoveShipmentRateArea() { }) // setup Fairbanks and Anchorage to have same RateArea - rateArea1 := setupRateAreaToManyPostalCodesData(*contract, []string{fairbanksAlaskaPostalCode, anchorageAlaskaPostalCode}) + rateAreaAK1 := setupRateAreaToManyPostalCodesData(*contract, []string{fairbanksAlaskaPostalCode, anchorageAlaskaPostalCode}) // setup Wasilla to have it's own RateArea - rateArea2 := setupRateAreaToPostalCodeData(setupRateArea(*contract), wasillaAlaskaPostalCode) + rateAreaAK2 := setupRateAreaToPostalCodeData(setupRateArea(*contract), wasillaAlaskaPostalCode) rateAreaCA, err := setupDomesticRateAreaAndZip3s("US88", "California-South", map[string]string{beverlyHillsCAPostalCode: "Beverly Hills", sanDiegoCAPostalCode: "San Diego"}, domServiceArea) if err != nil { @@ -226,27 +230,30 @@ func (suite *MTOShipmentServiceSuite) TestGetMoveShipmentRateArea() { shipmentPostalCodeRateAreas, err := shipmentRateAreaFetcher.GetPrimeMoveShipmentRateAreas(suite.AppContextForTest(), testMove) suite.NotNil(shipmentPostalCodeRateAreas) suite.FatalNoError(err) - suite.Equal(5, len(*shipmentPostalCodeRateAreas)) + suite.Equal(4, len(*shipmentPostalCodeRateAreas)) - suite.Equal(true, isRateAreaEquals(rateArea1, fairbanksAlaskaPostalCode, shipmentPostalCodeRateAreas)) - suite.Equal(true, isRateAreaEquals(rateArea1, anchorageAlaskaPostalCode, shipmentPostalCodeRateAreas)) - suite.Equal(true, isRateAreaEquals(rateArea2, wasillaAlaskaPostalCode, shipmentPostalCodeRateAreas)) + suite.Equal(true, isRateAreaEquals(rateAreaAK1, fairbanksAlaskaPostalCode, shipmentPostalCodeRateAreas)) + suite.Equal(true, isRateAreaEquals(rateAreaAK1, anchorageAlaskaPostalCode, shipmentPostalCodeRateAreas)) + suite.Equal(true, isRateAreaEquals(rateAreaAK2, wasillaAlaskaPostalCode, shipmentPostalCodeRateAreas)) suite.Equal(true, isRateAreaEquals(rateAreaCA, beverlyHillsCAPostalCode, shipmentPostalCodeRateAreas)) - suite.Equal(true, isRateAreaEquals(rateAreaCA, sanDiegoCAPostalCode, shipmentPostalCodeRateAreas)) - suite.Equal(false, isRateAreaEquals(rateArea2, fairbanksAlaskaPostalCode, shipmentPostalCodeRateAreas)) - suite.Equal(false, isRateAreaEquals(rateArea2, anchorageAlaskaPostalCode, shipmentPostalCodeRateAreas)) - suite.Equal(false, isRateAreaEquals(rateArea1, wasillaAlaskaPostalCode, shipmentPostalCodeRateAreas)) + // Postal code used only in a CONUS shipment should not have been fetched + i := slices.IndexFunc(*shipmentPostalCodeRateAreas, func(pcra services.ShipmentPostalCodeRateArea) bool { + return pcra.PostalCode == sanDiegoCAPostalCode + }) + suite.Equal(-1, i) + + suite.Equal(false, isRateAreaEquals(rateAreaAK1, beverlyHillsCAPostalCode, shipmentPostalCodeRateAreas)) + suite.Equal(false, isRateAreaEquals(rateAreaAK1, wasillaAlaskaPostalCode, shipmentPostalCodeRateAreas)) + suite.Equal(false, isRateAreaEquals(rateAreaAK2, fairbanksAlaskaPostalCode, shipmentPostalCodeRateAreas)) + suite.Equal(false, isRateAreaEquals(rateAreaAK2, anchorageAlaskaPostalCode, shipmentPostalCodeRateAreas)) + suite.Equal(false, isRateAreaEquals(rateAreaAK2, beverlyHillsCAPostalCode, shipmentPostalCodeRateAreas)) suite.Equal(false, isRateAreaEquals(rateAreaCA, fairbanksAlaskaPostalCode, shipmentPostalCodeRateAreas)) suite.Equal(false, isRateAreaEquals(rateAreaCA, anchorageAlaskaPostalCode, shipmentPostalCodeRateAreas)) suite.Equal(false, isRateAreaEquals(rateAreaCA, wasillaAlaskaPostalCode, shipmentPostalCodeRateAreas)) - suite.Equal(false, isRateAreaEquals(rateArea1, beverlyHillsCAPostalCode, shipmentPostalCodeRateAreas)) - suite.Equal(false, isRateAreaEquals(rateArea1, sanDiegoCAPostalCode, shipmentPostalCodeRateAreas)) - suite.Equal(false, isRateAreaEquals(rateArea2, beverlyHillsCAPostalCode, shipmentPostalCodeRateAreas)) - suite.Equal(false, isRateAreaEquals(rateArea2, sanDiegoCAPostalCode, shipmentPostalCodeRateAreas)) }) - suite.Run("Returns matching CONUS rate areas", func() { + suite.Run("Does not return rate areas for CONUS only shipments", func() { availableToPrimeAtTime := time.Now().Add(-500 * time.Hour) testMove := models.Move{ AvailableToPrimeAt: &availableToPrimeAtTime, @@ -266,6 +273,7 @@ func (suite *MTOShipmentServiceSuite) TestGetMoveShipmentRateArea() { PostalCode: sanDiegoCAPostalCode, IsOconus: models.BoolPointer(false), }, + MarketCode: models.MarketCodeDomestic, }, models.MTOShipment{ PPMShipment: &models.PPMShipment{ @@ -284,6 +292,7 @@ func (suite *MTOShipmentServiceSuite) TestGetMoveShipmentRateArea() { IsOconus: models.BoolPointer(false), }, }, + MarketCode: models.MarketCodeDomestic, }, }, } @@ -334,29 +343,20 @@ func (suite *MTOShipmentServiceSuite) TestGetMoveShipmentRateArea() { return rateArea, nil } - rateAreaCA, err := setupDomesticRateAreaAndZip3s("US88", "California-South", map[string]string{beverlyHillsCAPostalCode: "Beverly Hills", sanDiegoCAPostalCode: "San Diego"}, domServiceArea) + _, err := setupDomesticRateAreaAndZip3s("US88", "California-South", map[string]string{beverlyHillsCAPostalCode: "Beverly Hills", sanDiegoCAPostalCode: "San Diego"}, domServiceArea) if err != nil { suite.Fail(err.Error()) } - rateAreaNY, err := setupDomesticRateAreaAndZip3s("US17", "New York", map[string]string{brooklynNYPostalCode: "Brooklyn"}, domServiceArea) + _, err = setupDomesticRateAreaAndZip3s("US17", "New York", map[string]string{brooklynNYPostalCode: "Brooklyn"}, domServiceArea) if err != nil { suite.Fail(err.Error()) } shipmentPostalCodeRateAreas, err := shipmentRateAreaFetcher.GetPrimeMoveShipmentRateAreas(suite.AppContextForTest(), testMove) suite.NotNil(shipmentPostalCodeRateAreas) - suite.Equal(3, len(*shipmentPostalCodeRateAreas)) + suite.Equal(0, len(*shipmentPostalCodeRateAreas)) suite.Nil(err) - - var shipmentPostalCodeRateAreaLookupMap = make(map[string]services.ShipmentPostalCodeRateArea) - for _, pcra := range *shipmentPostalCodeRateAreas { - shipmentPostalCodeRateAreaLookupMap[pcra.PostalCode] = pcra - } - - suite.Equal(rateAreaCA.Name, shipmentPostalCodeRateAreaLookupMap[beverlyHillsCAPostalCode].RateArea.Name) - suite.Equal(rateAreaCA.Name, shipmentPostalCodeRateAreaLookupMap[sanDiegoCAPostalCode].RateArea.Name) - suite.Equal(rateAreaNY.Name, shipmentPostalCodeRateAreaLookupMap[brooklynNYPostalCode].RateArea.Name) }) suite.Run("not available to prime error", func() { From 3821ff9799b397d65efd6059dbfc0dc6188608d3 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Wed, 26 Feb 2025 19:10:55 +0000 Subject: [PATCH 26/26] update tests, fix storybook --- pkg/models/ppm_shipment_test.go | 3 +++ src/components/Table/TableQueue.stories.jsx | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/pkg/models/ppm_shipment_test.go b/pkg/models/ppm_shipment_test.go index 7dbeb27f545..f4b2662a98b 100644 --- a/pkg/models/ppm_shipment_test.go +++ b/pkg/models/ppm_shipment_test.go @@ -29,6 +29,7 @@ func (suite *ModelSuite) TestPPMShipmentValidation() { }{ "Successful Minimal Validation": { ppmShipment: models.PPMShipment{ + PPMType: models.PPMTypeIncentiveBased, ShipmentID: uuid.Must(uuid.NewV4()), ExpectedDepartureDate: testdatagen.PeakRateCycleStart, Status: models.PPMShipmentStatusDraft, @@ -39,6 +40,7 @@ func (suite *ModelSuite) TestPPMShipmentValidation() { }, "Missing Required Fields": { ppmShipment: models.PPMShipment{ + PPMType: models.PPMTypeIncentiveBased, PickupAddressID: models.UUIDPointer(uuid.Nil), DestinationAddressID: models.UUIDPointer(uuid.Nil), }, @@ -53,6 +55,7 @@ func (suite *ModelSuite) TestPPMShipmentValidation() { "Optional fields raise errors with invalid values": { ppmShipment: models.PPMShipment{ // Setting up min required fields here so that we don't get these in our errors. + PPMType: models.PPMTypeIncentiveBased, ShipmentID: uuid.Must(uuid.NewV4()), ExpectedDepartureDate: testdatagen.PeakRateCycleStart, Status: models.PPMShipmentStatusDraft, diff --git a/src/components/Table/TableQueue.stories.jsx b/src/components/Table/TableQueue.stories.jsx index 346e67fb7dd..94d9321a75d 100644 --- a/src/components/Table/TableQueue.stories.jsx +++ b/src/components/Table/TableQueue.stories.jsx @@ -10,6 +10,7 @@ import { BRANCH_OPTIONS, MOVE_STATUS_OPTIONS } from 'constants/queues'; import SelectFilter from 'components/Table/Filters/SelectFilter'; import DateSelectFilter from 'components/Table/Filters/DateSelectFilter'; import { store } from 'shared/store'; +import { MockProviders } from 'testUtils'; export default { title: 'Office Components/Table', @@ -87,25 +88,32 @@ const defaultProps = { export const TXOTable = () => (
- + + +
); export const TXOTableSortable = () => (
- + + +
); export const TXOTableFilters = () => (
- + + +
); export const TXOTablePagination = () => (
- {' '} - + + +
);