diff --git a/pkg/gen/primev3api/embedded_spec.go b/pkg/gen/primev3api/embedded_spec.go index ec655b5ca41..b35aaf504b1 100644 --- a/pkg/gen/primev3api/embedded_spec.go +++ b/pkg/gen/primev3api/embedded_spec.go @@ -1641,6 +1641,31 @@ func init() { } ] }, + "MTOServiceItemInternationalFuelSurcharge": { + "description": "Describes a international Port of Embarkation/Debarkation fuel surcharge service item subtype of a MTOServiceItem.", + "allOf": [ + { + "$ref": "#/definitions/MTOServiceItem" + }, + { + "type": "object", + "properties": { + "portCode": { + "description": "A unique code for a Port", + "type": "string" + }, + "reServiceCode": { + "description": "A unique code for the service item. Indicates if the service is for Port of Embarkation (POEFSC) or Port of Debarkation (PODFSC).", + "type": "string", + "enum": [ + "PODFSC", + "POEFSC" + ] + } + } + } + ] + }, "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", "type": "string", @@ -5962,6 +5987,31 @@ func init() { } ] }, + "MTOServiceItemInternationalFuelSurcharge": { + "description": "Describes a international Port of Embarkation/Debarkation fuel surcharge service item subtype of a MTOServiceItem.", + "allOf": [ + { + "$ref": "#/definitions/MTOServiceItem" + }, + { + "type": "object", + "properties": { + "portCode": { + "description": "A unique code for a Port", + "type": "string" + }, + "reServiceCode": { + "description": "A unique code for the service item. Indicates if the service is for Port of Embarkation (POEFSC) or Port of Debarkation (PODFSC).", + "type": "string", + "enum": [ + "PODFSC", + "POEFSC" + ] + } + } + } + ] + }, "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", "type": "string", diff --git a/pkg/gen/primev3messages/m_t_o_service_item.go b/pkg/gen/primev3messages/m_t_o_service_item.go index 9559114c004..d6861255a80 100644 --- a/pkg/gen/primev3messages/m_t_o_service_item.go +++ b/pkg/gen/primev3messages/m_t_o_service_item.go @@ -279,6 +279,12 @@ func unmarshalMTOServiceItem(data []byte, consumer runtime.Consumer) (MTOService 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 "MTOServiceItemOriginSIT": var result MTOServiceItemOriginSIT if err := consumer.Consume(buf2, &result); err != nil { diff --git a/pkg/gen/primev3messages/m_t_o_service_item_international_fuel_surcharge.go b/pkg/gen/primev3messages/m_t_o_service_item_international_fuel_surcharge.go new file mode 100644 index 00000000000..73d7c4e4c0b --- /dev/null +++ b/pkg/gen/primev3messages/m_t_o_service_item_international_fuel_surcharge.go @@ -0,0 +1,575 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package primev3messages + +// 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" +) + +// MTOServiceItemInternationalFuelSurcharge Describes a international Port of Embarkation/Debarkation fuel surcharge service item subtype of a MTOServiceItem. +// +// swagger:model MTOServiceItemInternationalFuelSurcharge +type MTOServiceItemInternationalFuelSurcharge struct { + eTagField string + + idField strfmt.UUID + + lockedPriceCentsField *int64 + + moveTaskOrderIdField *strfmt.UUID + + mtoShipmentIdField strfmt.UUID + + reServiceNameField string + + rejectionReasonField *string + + serviceRequestDocumentsField ServiceRequestDocuments + + statusField MTOServiceItemStatus + + // A unique code for a Port + PortCode string `json:"portCode,omitempty"` + + // A unique code for the service item. Indicates if the service is for Port of Embarkation (POEFSC) or Port of Debarkation (PODFSC). + // Enum: [PODFSC POEFSC] + ReServiceCode string `json:"reServiceCode,omitempty"` +} + +// ETag gets the e tag of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) ETag() string { + return m.eTagField +} + +// SetETag sets the e tag of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) SetETag(val string) { + m.eTagField = val +} + +// ID gets the id of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) ID() strfmt.UUID { + return m.idField +} + +// SetID sets the id of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) SetID(val strfmt.UUID) { + m.idField = val +} + +// LockedPriceCents gets the locked price cents of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) LockedPriceCents() *int64 { + return m.lockedPriceCentsField +} + +// SetLockedPriceCents sets the locked price cents of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) SetLockedPriceCents(val *int64) { + m.lockedPriceCentsField = val +} + +// ModelType gets the model type of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) ModelType() MTOServiceItemModelType { + return "MTOServiceItemInternationalFuelSurcharge" +} + +// SetModelType sets the model type of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) SetModelType(val MTOServiceItemModelType) { +} + +// MoveTaskOrderID gets the move task order ID of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) MoveTaskOrderID() *strfmt.UUID { + return m.moveTaskOrderIdField +} + +// SetMoveTaskOrderID sets the move task order ID of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) SetMoveTaskOrderID(val *strfmt.UUID) { + m.moveTaskOrderIdField = val +} + +// MtoShipmentID gets the mto shipment ID of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) MtoShipmentID() strfmt.UUID { + return m.mtoShipmentIdField +} + +// SetMtoShipmentID sets the mto shipment ID of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) SetMtoShipmentID(val strfmt.UUID) { + m.mtoShipmentIdField = val +} + +// ReServiceName gets the re service name of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) ReServiceName() string { + return m.reServiceNameField +} + +// SetReServiceName sets the re service name of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) SetReServiceName(val string) { + m.reServiceNameField = val +} + +// RejectionReason gets the rejection reason of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) RejectionReason() *string { + return m.rejectionReasonField +} + +// SetRejectionReason sets the rejection reason of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) SetRejectionReason(val *string) { + m.rejectionReasonField = val +} + +// ServiceRequestDocuments gets the service request documents of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) ServiceRequestDocuments() ServiceRequestDocuments { + return m.serviceRequestDocumentsField +} + +// SetServiceRequestDocuments sets the service request documents of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) SetServiceRequestDocuments(val ServiceRequestDocuments) { + m.serviceRequestDocumentsField = val +} + +// Status gets the status of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) Status() MTOServiceItemStatus { + return m.statusField +} + +// SetStatus sets the status of this subtype +func (m *MTOServiceItemInternationalFuelSurcharge) SetStatus(val MTOServiceItemStatus) { + m.statusField = val +} + +// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure +func (m *MTOServiceItemInternationalFuelSurcharge) UnmarshalJSON(raw []byte) error { + var data struct { + + // A unique code for a Port + PortCode string `json:"portCode,omitempty"` + + // A unique code for the service item. Indicates if the service is for Port of Embarkation (POEFSC) or Port of Debarkation (PODFSC). + // Enum: [PODFSC POEFSC] + ReServiceCode string `json:"reServiceCode,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 MTOServiceItemInternationalFuelSurcharge + + 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.PortCode = data.PortCode + result.ReServiceCode = data.ReServiceCode + + *m = result + + return nil +} + +// MarshalJSON marshals this object with a polymorphic type to a JSON structure +func (m MTOServiceItemInternationalFuelSurcharge) MarshalJSON() ([]byte, error) { + var b1, b2, b3 []byte + var err error + b1, err = json.Marshal(struct { + + // A unique code for a Port + PortCode string `json:"portCode,omitempty"` + + // A unique code for the service item. Indicates if the service is for Port of Embarkation (POEFSC) or Port of Debarkation (PODFSC). + // Enum: [PODFSC POEFSC] + ReServiceCode string `json:"reServiceCode,omitempty"` + }{ + + PortCode: m.PortCode, + + ReServiceCode: m.ReServiceCode, + }) + 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 fuel surcharge +func (m *MTOServiceItemInternationalFuelSurcharge) 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 len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *MTOServiceItemInternationalFuelSurcharge) 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 *MTOServiceItemInternationalFuelSurcharge) 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 *MTOServiceItemInternationalFuelSurcharge) 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 *MTOServiceItemInternationalFuelSurcharge) 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 *MTOServiceItemInternationalFuelSurcharge) 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 mTOServiceItemInternationalFuelSurchargeTypeReServiceCodePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["PODFSC","POEFSC"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + mTOServiceItemInternationalFuelSurchargeTypeReServiceCodePropEnum = append(mTOServiceItemInternationalFuelSurchargeTypeReServiceCodePropEnum, v) + } +} + +// property enum +func (m *MTOServiceItemInternationalFuelSurcharge) validateReServiceCodeEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, mTOServiceItemInternationalFuelSurchargeTypeReServiceCodePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *MTOServiceItemInternationalFuelSurcharge) validateReServiceCode(formats strfmt.Registry) error { + + if swag.IsZero(m.ReServiceCode) { // not required + return nil + } + + // value enum + if err := m.validateReServiceCodeEnum("reServiceCode", "body", m.ReServiceCode); err != nil { + return err + } + + return nil +} + +// ContextValidate validate this m t o service item international fuel surcharge based on the context it is used +func (m *MTOServiceItemInternationalFuelSurcharge) 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 len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *MTOServiceItemInternationalFuelSurcharge) 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 *MTOServiceItemInternationalFuelSurcharge) 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 *MTOServiceItemInternationalFuelSurcharge) 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 *MTOServiceItemInternationalFuelSurcharge) 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 *MTOServiceItemInternationalFuelSurcharge) 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 *MTOServiceItemInternationalFuelSurcharge) 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 *MTOServiceItemInternationalFuelSurcharge) 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 +} + +// MarshalBinary interface implementation +func (m *MTOServiceItemInternationalFuelSurcharge) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *MTOServiceItemInternationalFuelSurcharge) UnmarshalBinary(b []byte) error { + var res MTOServiceItemInternationalFuelSurcharge + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/handlers/primeapiv3/payloads/model_to_payload.go b/pkg/handlers/primeapiv3/payloads/model_to_payload.go index b4bfaf6faa8..c753b86fabc 100644 --- a/pkg/handlers/primeapiv3/payloads/model_to_payload.go +++ b/pkg/handlers/primeapiv3/payloads/model_to_payload.go @@ -847,6 +847,19 @@ func MTOServiceItem(mtoServiceItem *models.MTOServiceItem) primev3messages.MTOSe EstimatedWeight: handlers.FmtPoundPtr(mtoServiceItem.EstimatedWeight), ActualWeight: handlers.FmtPoundPtr(mtoServiceItem.ActualWeight), } + + case models.ReServiceCodePODFSC, models.ReServiceCodePOEFSC: + var portCode string + if mtoServiceItem.POELocation != nil { + portCode = mtoServiceItem.POELocation.Port.PortCode + } else if mtoServiceItem.PODLocation != nil { + portCode = mtoServiceItem.PODLocation.Port.PortCode + } + payload = &primev3messages.MTOServiceItemInternationalFuelSurcharge{ + ReServiceCode: string(mtoServiceItem.ReService.Code), + PortCode: portCode, + } + default: // otherwise, basic service item payload = &primev3messages.MTOServiceItemBasic{ diff --git a/src/constants/serviceItems.js b/src/constants/serviceItems.js index 7a1a0f76502..cb8e35c95bf 100644 --- a/src/constants/serviceItems.js +++ b/src/constants/serviceItems.js @@ -165,7 +165,7 @@ const SERVICE_ITEMS_ALLOWED_WEIGHT_BILLED_PARAM = [ SERVICE_ITEM_CODES.FSC, ]; -const SIT_SERVICE_ITEMS_ALLOWED_UPDATE = [ +const SERVICE_ITEMS_ALLOWED_UPDATE = [ SERVICE_ITEM_CODES.DDDSIT, SERVICE_ITEM_CODES.DDASIT, SERVICE_ITEM_CODES.DOASIT, @@ -174,6 +174,8 @@ const SIT_SERVICE_ITEMS_ALLOWED_UPDATE = [ SERVICE_ITEM_CODES.DDFSIT, SERVICE_ITEM_CODES.DOSFSC, SERVICE_ITEM_CODES.DDSFSC, + SERVICE_ITEM_CODES.PODFSC, + SERVICE_ITEM_CODES.POEFSC, ]; /** @@ -228,5 +230,5 @@ export { allowedServiceItemCalculations, SERVICE_ITEM_STATUSES, SERVICE_ITEMS_ALLOWED_WEIGHT_BILLED_PARAM, - SIT_SERVICE_ITEMS_ALLOWED_UPDATE, + SERVICE_ITEMS_ALLOWED_UPDATE, }; diff --git a/src/pages/Office/index.jsx b/src/pages/Office/index.jsx index 8ae42ba6af5..8a945c4c7f8 100644 --- a/src/pages/Office/index.jsx +++ b/src/pages/Office/index.jsx @@ -83,9 +83,7 @@ const PrimeSimulatorUploadServiceRequestDocuments = lazy(() => import('pages/PrimeUI/UploadServiceRequestDocuments/UploadServiceRequestDocuments'), ); const PrimeSimulatorCreateServiceItem = lazy(() => import('pages/PrimeUI/CreateServiceItem/CreateServiceItem')); -const PrimeSimulatorUpdateSitServiceItem = lazy(() => - import('pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateSitServiceItem'), -); +const PrimeSimulatorUpdateServiceItem = lazy(() => import('pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateServiceItem')); const PrimeUIShipmentUpdateAddress = lazy(() => import('pages/PrimeUI/Shipment/PrimeUIShipmentUpdateAddress')); const PrimeUIShipmentUpdateReweigh = lazy(() => import('pages/PrimeUI/Shipment/PrimeUIShipmentUpdateReweigh')); const PrimeSimulatorCreateSITExtensionRequest = lazy(() => @@ -534,11 +532,11 @@ export class OfficeApp extends Component { } /> - + } /> diff --git a/src/pages/PrimeUI/MoveTaskOrder/MoveDetails.jsx b/src/pages/PrimeUI/MoveTaskOrder/MoveDetails.jsx index 92f5ec9bd55..45881d58602 100644 --- a/src/pages/PrimeUI/MoveTaskOrder/MoveDetails.jsx +++ b/src/pages/PrimeUI/MoveTaskOrder/MoveDetails.jsx @@ -21,7 +21,7 @@ import { usePrimeSimulatorGetMove } from 'hooks/queries'; import { completeCounseling, deleteShipment, downloadMoveOrder } from 'services/primeApi'; import { setFlashMessage as setFlashMessageAction } from 'store/flash/actions'; import scrollToTop from 'shared/scrollToTop'; -import { SIT_SERVICE_ITEMS_ALLOWED_UPDATE } from 'constants/serviceItems'; +import { SERVICE_ITEMS_ALLOWED_UPDATE } from 'constants/serviceItems'; import { MoveOrderDocumentType } from 'shared/constants'; import { CHECK_SPECIAL_ORDERS_TYPES, SPECIAL_ORDERS_TYPES } from 'constants/orders'; @@ -257,7 +257,7 @@ const MoveDetails = ({ setFlashMessage }) => { {serviceItem.reServiceCode} - {serviceItem.reServiceName}
- {SIT_SERVICE_ITEMS_ALLOWED_UPDATE.includes(serviceItem.reServiceCode) ? ( + {SERVICE_ITEMS_ALLOWED_UPDATE.includes(serviceItem.reServiceCode) ? ( { ); }); - it('shows edit button next to the right destination SIT service items', async () => { + it('shows edit button next to the right service items', async () => { usePrimeSimulatorGetMove.mockReturnValue(moveReturnValue); renderWithProviders(); // Check for Edit buttons const editButtons = screen.getAllByRole('link', { name: 'Edit' }); - expect(editButtons).toHaveLength(3); + expect(editButtons).toHaveLength(5); }); }); }); diff --git a/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalFuelSurcharge.module.scss b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalFuelSurcharge.module.scss new file mode 100644 index 00000000000..985dcabf2b0 --- /dev/null +++ b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalFuelSurcharge.module.scss @@ -0,0 +1,12 @@ +@import 'shared/styles/basics'; +@import 'shared/styles/colors'; + +.IntlFsc { + + .intlFscHeader { + text-align: center; + } + + @include u-margin-bottom(4); + + } diff --git a/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalFuelSurchargeForm.jsx b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalFuelSurchargeForm.jsx new file mode 100644 index 00000000000..63014e86300 --- /dev/null +++ b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalFuelSurchargeForm.jsx @@ -0,0 +1,144 @@ +import React from 'react'; +import { Formik } from 'formik'; +import * as Yup from 'yup'; +import { useNavigate, useParams, generatePath } from 'react-router-dom'; +import { FormGroup } from '@trussworks/react-uswds'; +import classnames from 'classnames'; + +import styles from './PrimeUIUpdateInternationalFuelSurcharge.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 { SERVICE_ITEM_STATUSES } from 'constants/serviceItems'; + +const PrimeUIUpdateInternationalFuelSurchargeForm = ({ onUpdateServiceItem, moveTaskOrder, mtoServiceItemId }) => { + const { moveCodeOrID } = useParams(); + const navigate = useNavigate(); + + const handleClose = () => { + navigate(generatePath(primeSimulatorRoutes.VIEW_MOVE_PATH, { moveCodeOrID })); + }; + + const serviceItem = moveTaskOrder?.mtoServiceItems.find((s) => s?.id === mtoServiceItemId); + const mtoShipment = moveTaskOrder?.mtoShipments.find((s) => s.id === serviceItem.mtoShipmentID); + let port; + if (mtoShipment.portOfEmbarkation) { + port = mtoShipment.portOfEmbarkation; + } else if (mtoShipment.portOfDebarkation) { + port = mtoShipment.portOfDebarkation; + } else { + port = null; + } + const initialValues = { + mtoServiceItemID: serviceItem.id, + reServiceCode: serviceItem.reServiceCode, + eTag: serviceItem.eTag, + portCode: port?.portCode, + }; + + const onSubmit = (values) => { + const { eTag, mtoServiceItemID, portCode, reServiceCode } = values; + + const body = { + portCode, + reServiceCode, + modelType: 'UpdateMTOServiceItemInternationalPortFSC', + }; + + onUpdateServiceItem({ mtoServiceItemID, eTag, body }); + }; + + return ( + + {({ handleSubmit, setFieldValue }) => ( +
+ +
+

Update International Fuel Surcharge Service Item

+ +
+ Here you can update specific fields for an International Fuel Surcharge service item.
+ At this time, only the following values can be updated:
{' '} + + Port Code
+
+
+
+ +

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

+
+
+
ID:
+
{serviceItem.id}
+
+
+
MTO ID:
+
{serviceItem.moveTaskOrderID}
+
+
+
Shipment ID:
+
{serviceItem.mtoShipmentID}
+
+
+
Status:
+
{serviceItem.status}
+
+
+
Port:
+
{port && port.portName}
+
+
+
Port Location:
+
+ {port && port.city} + {port && port.city && ','} {port && port.state} {port && port.zip} +
+
+
+ { + setFieldValue('portCode', e.target.value.toUpperCase()); + }} + /> +
+ +
+
+
+ )} +
+ ); +}; + +export default PrimeUIUpdateInternationalFuelSurchargeForm; diff --git a/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalFuelSurchargeForm.test.jsx b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalFuelSurchargeForm.test.jsx new file mode 100644 index 00000000000..78461a6e840 --- /dev/null +++ b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateInternationalFuelSurchargeForm.test.jsx @@ -0,0 +1,157 @@ +import React from 'react'; +import { screen, fireEvent } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import PrimeUIUpdateInternationalFuelSurchargeForm from './PrimeUIUpdateInternationalFuelSurchargeForm'; + +import { renderWithProviders } from 'testUtils'; +import { primeSimulatorRoutes } from 'constants/routes'; + +const mtoServiceItemID = '38569958-2889-41e5-8101-82c56ec48430'; + +const serviceItem = { + id: mtoServiceItemID, + reServiceCode: 'POEFSC', + reServiceName: 'International POE Fuel Surcharge', + status: 'APPROVED', + mtoShipmentID: '38569958-2889-41e5-8102-82c56ec48430', +}; + +const portOfEmbarkation = { + city: 'SEATTLE', + country: 'UNITED STATES', + county: 'KING', + id: '38569958-2889-41e5-8101-82c56ec48430', + portCode: 'SEA', + portName: 'SEATTLE TACOMA INTL', + portType: 'A', + state: 'WASHINGTON', + zip: '98158', +}; + +const mtoShipment = { + id: '38569958-2889-41e5-8102-82c56ec48430', + portOfEmbarkation, +}; + +const moveTaskOrder = { + mtoShipments: [mtoShipment], + mtoServiceItems: [serviceItem], + serviceItem, +}; + +const onUpdateServiceItemMock = jest.fn(); + +// Mock the react-router-dom functions +const mockNavigate = jest.fn(); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: () => mockNavigate, + useParams: jest.fn().mockReturnValue({ moveCodeOrID: ':moveCodeOrID' }), +})); + +describe('PrimeUIUpdateInternationalFuelSurchargeForm', () => { + it('renders the international fuel surcharge form', async () => { + renderWithProviders( + , + ); + + expect( + screen.getByRole('heading', { name: 'Update International Fuel Surcharge Service Item', level: 2 }), + ).toBeInTheDocument(); + expect( + screen.getByRole('heading', { name: 'POEFSC - International POE Fuel Surcharge', level: 3 }), + ).toBeInTheDocument(); + expect(screen.getByText('Port:')).toBeInTheDocument(); + expect(screen.getByText('SEATTLE TACOMA INTL')).toBeInTheDocument(); + expect(screen.getByText('SEATTLE, WASHINGTON 98158')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Save' })).toBeEnabled(); + expect(screen.getByRole('button', { name: 'Cancel' })).toBeEnabled(); + }); + + it('fires off onUpdateServiceItemMock function when save button is clicked', async () => { + renderWithProviders( + , + ); + const portCodeInput = screen.getByLabelText(/Port Code/); + await userEvent.type(portCodeInput, 'SEA'); + const saveButton = await screen.findByRole('button', { name: 'Save' }); + + await userEvent.click(saveButton); + + expect(onUpdateServiceItemMock).toHaveBeenCalled(); + }); + + it('port code value is set to uppercase when user types in a value', async () => { + renderWithProviders( + , + ); + const portCodeInput = screen.getByLabelText(/Port Code/); + expect(portCodeInput).toHaveValue('SEA'); + fireEvent.change(portCodeInput, { target: { value: 'pdx' } }); + + expect(portCodeInput).toHaveValue('PDX'); + }); + + it('does not fire off onUpdateServiceItemMock function when save button is clicked and port code is empty', async () => { + renderWithProviders( + , + ); + const portCodeInput = screen.getByLabelText(/Port Code/); + await userEvent.clear(portCodeInput, ''); + const saveButton = await screen.findByRole('button', { name: 'Save' }); + + onUpdateServiceItemMock.mockClear(); + await userEvent.click(saveButton); + expect(onUpdateServiceItemMock).not.toHaveBeenCalled(); + }); + + it('does not fire off onUpdateServiceItemMock function when save button is clicked and port code is fewer than 3 characters', async () => { + renderWithProviders( + , + ); + const portCodeInput = screen.getByLabelText(/Port Code/); + await userEvent.clear(portCodeInput, '12'); + const saveButton = await screen.findByRole('button', { name: 'Save' }); + + onUpdateServiceItemMock.mockClear(); + await userEvent.click(saveButton); + expect(onUpdateServiceItemMock).not.toHaveBeenCalled(); + }); + + it('directs the user back to the move page when cancel button is clicked', async () => { + renderWithProviders( + , + ); + + const cancelButton = await screen.findByRole('button', { name: 'Cancel' }); + + await userEvent.click(cancelButton); + + expect(mockNavigate).toHaveBeenCalledWith(primeSimulatorRoutes.VIEW_MOVE_PATH); + }); +}); diff --git a/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateSitServiceItem.jsx b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateServiceItem.jsx similarity index 61% rename from src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateSitServiceItem.jsx rename to src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateServiceItem.jsx index 0a39bc1e6a3..387981922c4 100644 --- a/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateSitServiceItem.jsx +++ b/src/pages/PrimeUI/UpdateServiceItems/PrimeUIUpdateServiceItem.jsx @@ -6,6 +6,7 @@ import { connect } from 'react-redux'; import PrimeUIUpdateOriginSITForm from './PrimeUIUpdateOriginSITForm'; import PrimeUIUpdateDestSITForm from './PrimeUIUpdateDestSITForm'; +import PrimeUIUpdateInternationalFuelSurchargeForm from './PrimeUIUpdateInternationalFuelSurchargeForm'; import { updateMTOServiceItem } from 'services/primeApi'; import scrollToTop from 'shared/scrollToTop'; @@ -19,18 +20,18 @@ import { primeSimulatorRoutes } from 'constants/routes'; import { formatDateForSwagger, formatDateWithUTC } from 'shared/dates'; import { SERVICE_ITEM_STATUSES } from 'constants/serviceItems'; -const PrimeUIUpdateSitServiceItem = ({ setFlashMessage }) => { +const PrimeUIUpdateServiceItem = ({ setFlashMessage }) => { const [errorMessage, setErrorMessage] = useState(); const navigate = useNavigate(); const { moveCodeOrID, mtoServiceItemId } = useParams(); const { moveTaskOrder, isLoading, isError } = usePrimeSimulatorGetMove(moveCodeOrID); /* istanbul ignore next */ - const { mutate: createUpdateSITServiceItemRequestMutation } = useMutation(updateMTOServiceItem, { + const { mutate: createUpdateServiceItemRequestMutation } = useMutation(updateMTOServiceItem, { onSuccess: () => { setFlashMessage( - `UPDATE_SIT_SERVICE_ITEM_REQUEST_SUCCESS${moveCodeOrID}`, + `UPDATE_SERVICE_ITEM_REQUEST_SUCCESS${moveCodeOrID}`, 'success', - 'Successfully updated SIT service item', + 'Successfully updated service item', '', true, ); @@ -68,44 +69,48 @@ const PrimeUIUpdateSitServiceItem = ({ setFlashMessage }) => { const serviceItem = moveTaskOrder?.mtoServiceItems.find((s) => s?.id === mtoServiceItemId); const { modelType } = serviceItem; - - const initialValues = { - sitDepartureDate: formatDateWithUTC(serviceItem.sitDepartureDate, 'YYYY-MM-DD', 'DD MMM YYYY') || '', - sitRequestedDelivery: formatDateWithUTC(serviceItem.sitRequestedDelivery, 'YYYY-MM-DD', 'DD MMM YYYY') || '', - sitCustomerContacted: formatDateWithUTC(serviceItem.sitCustomerContacted, 'YYYY-MM-DD', 'DD MMM YYYY') || '', - mtoServiceItemID: serviceItem.id, - reServiceCode: serviceItem.reServiceCode, - eTag: serviceItem.eTag, - }; - - // sending the data submitted in the form to the API - // if any of the dates are skipped or not filled with values, we'll just make them null - const onSubmit = (values) => { - const { - sitCustomerContacted, - sitDepartureDate, - sitRequestedDelivery, - updateReason, - mtoServiceItemID, - reServiceCode, - eTag, - } = values; - - const body = { - sitDepartureDate: sitDepartureDate === 'Invalid date' ? null : formatDateForSwagger(sitDepartureDate), - sitRequestedDelivery: sitRequestedDelivery === 'Invalid date' ? null : formatDateForSwagger(sitRequestedDelivery), - sitCustomerContacted: sitCustomerContacted === 'Invalid date' ? null : formatDateForSwagger(sitCustomerContacted), - reServiceCode, - modelType: 'UpdateMTOServiceItemSIT', + let initialValues; + let onSubmit; + if (modelType === 'MTOServiceItemOriginSIT' || modelType === 'MTOServiceItemDestSIT') { + initialValues = { + sitDepartureDate: formatDateWithUTC(serviceItem.sitDepartureDate, 'YYYY-MM-DD', 'DD MMM YYYY') || '', + sitRequestedDelivery: formatDateWithUTC(serviceItem.sitRequestedDelivery, 'YYYY-MM-DD', 'DD MMM YYYY') || '', + sitCustomerContacted: formatDateWithUTC(serviceItem.sitCustomerContacted, 'YYYY-MM-DD', 'DD MMM YYYY') || '', + mtoServiceItemID: serviceItem.id, + reServiceCode: serviceItem.reServiceCode, + eTag: serviceItem.eTag, }; + // sending the data submitted in the form to the API + // if any of the dates are skipped or not filled with values, we'll just make them null + onSubmit = (values) => { + const { + sitCustomerContacted, + sitDepartureDate, + sitRequestedDelivery, + updateReason, + mtoServiceItemID, + reServiceCode, + eTag, + } = values; + + const body = { + sitDepartureDate: sitDepartureDate === 'Invalid date' ? null : formatDateForSwagger(sitDepartureDate), + sitRequestedDelivery: + sitRequestedDelivery === 'Invalid date' ? null : formatDateForSwagger(sitRequestedDelivery), + sitCustomerContacted: + sitCustomerContacted === 'Invalid date' ? null : formatDateForSwagger(sitCustomerContacted), + reServiceCode, + modelType: 'UpdateMTOServiceItemSIT', + }; + + if (serviceItem?.status === SERVICE_ITEM_STATUSES.REJECTED) { + body.requestApprovalsRequestedStatus = true; + body.updateReason = updateReason; + } - if (serviceItem?.status === SERVICE_ITEM_STATUSES.REJECTED) { - body.requestApprovalsRequestedStatus = true; - body.updateReason = updateReason; - } - - createUpdateSITServiceItemRequestMutation({ mtoServiceItemID, eTag, body }); - }; + createUpdateServiceItemRequestMutation({ mtoServiceItemID, eTag, body }); + }; + } return (
@@ -136,6 +141,13 @@ const PrimeUIUpdateSitServiceItem = ({ setFlashMessage }) => { onSubmit={onSubmit} /> ) : null} + {modelType === 'MTOServiceItemInternationalFuelSurcharge' ? ( + + ) : null} @@ -148,4 +160,4 @@ const mapDispatchToProps = { setFlashMessage: setFlashMessageAction, }; -export default connect(() => ({}), mapDispatchToProps)(PrimeUIUpdateSitServiceItem); +export default connect(() => ({}), mapDispatchToProps)(PrimeUIUpdateServiceItem); diff --git a/src/pages/PrimeUI/UpdateServiceItems/PrimeUpdateSitServiceItem.test.jsx b/src/pages/PrimeUI/UpdateServiceItems/PrimeUpdateSitServiceItem.test.jsx index 1f397cb700f..e7f21f94309 100644 --- a/src/pages/PrimeUI/UpdateServiceItems/PrimeUpdateSitServiceItem.test.jsx +++ b/src/pages/PrimeUI/UpdateServiceItems/PrimeUpdateSitServiceItem.test.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import PrimeUIUpdateSitServiceItem from './PrimeUIUpdateSitServiceItem'; +import PrimeUIUpdateSitServiceItem from './PrimeUIUpdateServiceItem'; import { MockProviders, ReactQueryWrapper } from 'testUtils'; import { primeSimulatorRoutes } from 'constants/routes'; diff --git a/swagger-def/prime_v3.yaml b/swagger-def/prime_v3.yaml index 202fd7081ba..5bce4298a3f 100644 --- a/swagger-def/prime_v3.yaml +++ b/swagger-def/prime_v3.yaml @@ -348,6 +348,8 @@ definitions: $ref: 'definitions/prime/MTOServiceItemOriginSIT.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 + $ref: 'definitions/prime/MTOServiceItemInternationalFuelSurcharge.yaml' CreateMTOShipment: type: object properties: diff --git a/swagger/prime_v3.yaml b/swagger/prime_v3.yaml index a87ccba5cb8..10692aa49f7 100644 --- a/swagger/prime_v3.yaml +++ b/swagger/prime_v3.yaml @@ -663,6 +663,25 @@ definitions: required: - reason - reServiceCode + MTOServiceItemInternationalFuelSurcharge: + description: >- + Describes a international Port of Embarkation/Debarkation fuel surcharge + service item subtype of a MTOServiceItem. + allOf: + - $ref: '#/definitions/MTOServiceItem' + - type: object + properties: + reServiceCode: + type: string + description: >- + A unique code for the service item. Indicates if the service is + for Port of Embarkation (POEFSC) or Port of Debarkation (PODFSC). + enum: + - PODFSC + - POEFSC + portCode: + description: A unique code for a Port + type: string CreateMTOShipment: type: object properties: