From 423c7ee5f1df89eba408a4c93eb3ff08043c78f7 Mon Sep 17 00:00:00 2001
From: JamesHawks224 <146897935+JamesHawks224@users.noreply.github.com>
Date: Wed, 27 Nov 2024 22:27:08 +0000
Subject: [PATCH 01/37] added alaska FF to filter order types
---
.../ServicesCounselingOrders.jsx | 22 ++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx
index 260b126ad7f..87bdf90d0c0 100644
--- a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx
+++ b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx
@@ -1,5 +1,5 @@
/* eslint-disable camelcase */
-import React, { useEffect, useReducer } from 'react';
+import React, { useEffect, useReducer, useState } from 'react';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { Button } from '@trussworks/react-uswds';
import { Formik } from 'formik';
@@ -8,6 +8,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import ordersFormValidationSchema from '../Orders/ordersFormValidationSchema';
+import { isBooleanFlagEnabled } from 'utils/featureFlags';
import styles from 'styles/documentViewerWithSidebar.module.scss';
import { milmoveLogger } from 'utils/milmoveLog';
import OrdersDetailForm from 'components/Office/OrdersDetailForm/OrdersDetailForm';
@@ -23,11 +24,10 @@ import SomethingWentWrong from 'shared/SomethingWentWrong';
import { LineOfAccountingDfasElementOrder } from 'types/lineOfAccounting';
import { LOA_VALIDATION_ACTIONS, reducer as loaReducer, initialState as initialLoaState } from 'reducers/loaValidation';
import { TAC_VALIDATION_ACTIONS, reducer as tacReducer, initialState as initialTacState } from 'reducers/tacValidation';
-import { LOA_TYPE, MOVE_DOCUMENT_TYPE } from 'shared/constants';
+import { LOA_TYPE, MOVE_DOCUMENT_TYPE, FEATURE_FLAG_KEYS } from 'shared/constants';
import DocumentViewerFileManager from 'components/DocumentViewerFileManager/DocumentViewerFileManager';
const deptIndicatorDropdownOptions = dropdownInputOptions(DEPARTMENT_INDICATOR_OPTIONS);
-const ordersTypeDropdownOptions = dropdownInputOptions(ORDERS_TYPE_OPTIONS);
const ordersTypeDetailsDropdownOptions = dropdownInputOptions(ORDERS_TYPE_DETAILS_OPTIONS);
const payGradeDropdownOptions = dropdownInputOptions(ORDERS_PAY_GRADE_OPTIONS);
@@ -38,6 +38,7 @@ const ServicesCounselingOrders = ({ files, amendedDocumentId, updateAmendedDocum
const [tacValidationState, tacValidationDispatch] = useReducer(tacReducer, null, initialTacState);
const [loaValidationState, loaValidationDispatch] = useReducer(loaReducer, null, initialLoaState);
const { move, orders, isLoading, isError } = useOrdersDocumentQueries(moveCode);
+ const [orderTypeOptions, setOrderTypeOptions] = useState(ORDERS_TYPE_OPTIONS);
const orderId = move?.ordersId;
const orderDocumentId = orders[orderId]?.uploaded_order_id;
@@ -246,6 +247,19 @@ const ServicesCounselingOrders = ({ files, amendedDocumentId, updateAmendedDocum
validateLoa,
]);
+ useEffect(() => {
+ const checkAlaskaFeatureFlag = async () => {
+ const isAlaskaEnabled = await isBooleanFlagEnabled(FEATURE_FLAG_KEYS.ENABLE_ALASKA);
+ if (!isAlaskaEnabled) {
+ const options = orderTypeOptions;
+ delete orderTypeOptions.EARLY_RETURN_OF_DEPENDENTS;
+ delete orderTypeOptions.STUDENT_TRAVEL;
+ setOrderTypeOptions(options);
+ }
+ };
+ checkAlaskaFeatureFlag();
+ }, [orderTypeOptions]);
+
if (isLoading) return ;
if (isError) return ;
@@ -291,6 +305,8 @@ const ServicesCounselingOrders = ({ files, amendedDocumentId, updateAmendedDocum
'Unable to find a LOA based on the provided details. Please ensure a department indicator and TAC are present on this form.';
const loaInvalidWarningMsg = 'The LOA identified based on the provided details appears to be invalid.';
+ const ordersTypeDropdownOptions = dropdownInputOptions(orderTypeOptions);
+
return (
From 3d0d83fe78d3ed7496c42e51c6491987f80e2654 Mon Sep 17 00:00:00 2001
From: JamesHawks224 <146897935+JamesHawks224@users.noreply.github.com>
Date: Mon, 9 Dec 2024 19:46:15 +0000
Subject: [PATCH 02/37] added hasDependents to 'order update by service
conselor' api.
---
pkg/gen/ghcapi/embedded_spec.go | 10 ++++++++++
pkg/gen/ghcmessages/counseling_update_order_payload.go | 3 +++
pkg/services/order/order_updater.go | 4 ++++
.../ServicesCounselingOrders.jsx | 3 ++-
swagger-def/ghc.yaml | 4 ++++
swagger/ghc.yaml | 4 ++++
6 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go
index 576c53137fb..22877e3ef0b 100644
--- a/pkg/gen/ghcapi/embedded_spec.go
+++ b/pkg/gen/ghcapi/embedded_spec.go
@@ -6842,6 +6842,11 @@ func init() {
"grade": {
"$ref": "#/definitions/Grade"
},
+ "hasDependents": {
+ "type": "boolean",
+ "title": "Are dependents included in your orders?",
+ "x-nullable": true
+ },
"issueDate": {
"description": "The date and time that these orders were cut.",
"type": "string",
@@ -23095,6 +23100,11 @@ func init() {
"grade": {
"$ref": "#/definitions/Grade"
},
+ "hasDependents": {
+ "type": "boolean",
+ "title": "Are dependents included in your orders?",
+ "x-nullable": true
+ },
"issueDate": {
"description": "The date and time that these orders were cut.",
"type": "string",
diff --git a/pkg/gen/ghcmessages/counseling_update_order_payload.go b/pkg/gen/ghcmessages/counseling_update_order_payload.go
index a03a99a22de..281972b5196 100644
--- a/pkg/gen/ghcmessages/counseling_update_order_payload.go
+++ b/pkg/gen/ghcmessages/counseling_update_order_payload.go
@@ -26,6 +26,9 @@ type CounselingUpdateOrderPayload struct {
// grade
Grade *Grade `json:"grade,omitempty"`
+ // Are dependents included in your orders?
+ HasDependents *bool `json:"hasDependents,omitempty"`
+
// Orders date
//
// The date and time that these orders were cut.
diff --git a/pkg/services/order/order_updater.go b/pkg/services/order/order_updater.go
index 2645a0bcea1..2969bab841f 100644
--- a/pkg/services/order/order_updater.go
+++ b/pkg/services/order/order_updater.go
@@ -400,6 +400,10 @@ func orderFromCounselingPayload(existingOrder models.Order, payload ghcmessages.
order.Entitlement.DBAuthorizedWeight = &weight
}
+ if payload.HasDependents != nil {
+ order.HasDependents = *payload.HasDependents
+ }
+
return order
}
diff --git a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx
index 87bdf90d0c0..86fe3879045 100644
--- a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx
+++ b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx
@@ -18,7 +18,7 @@ import { ORDERS } from 'constants/queryKeys';
import { servicesCounselingRoutes } from 'constants/routes';
import { useOrdersDocumentQueries } from 'hooks/queries';
import { getTacValid, getLoa, counselingUpdateOrder, getOrder } from 'services/ghcApi';
-import { formatSwaggerDate, dropdownInputOptions } from 'utils/formatters';
+import { formatSwaggerDate, dropdownInputOptions, formatYesNoAPIValue } from 'utils/formatters';
import LoadingPlaceholder from 'shared/LoadingPlaceholder';
import SomethingWentWrong from 'shared/SomethingWentWrong';
import { LineOfAccountingDfasElementOrder } from 'types/lineOfAccounting';
@@ -277,6 +277,7 @@ const ServicesCounselingOrders = ({ files, amendedDocumentId, updateAmendedDocum
reportByDate: formatSwaggerDate(values.reportByDate),
ordersType: values.ordersType,
grade: values.payGrade,
+ has_dependents: formatYesNoAPIValue('no'),
};
mutateOrders({ orderID: orderId, ifMatchETag: newOrderEtag, body: orderBody });
};
diff --git a/swagger-def/ghc.yaml b/swagger-def/ghc.yaml
index b8876b5c4e7..7fd0f2903c5 100644
--- a/swagger-def/ghc.yaml
+++ b/swagger-def/ghc.yaml
@@ -5579,6 +5579,10 @@ definitions:
$ref: definitions/NullableString.yaml
grade:
$ref: '#/definitions/Grade'
+ hasDependents:
+ type: boolean
+ title: Are dependents included in your orders?
+ x-nullable: true
required:
- issueDate
- reportByDate
diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml
index a93a2f7c820..61d62ddaa5f 100644
--- a/swagger/ghc.yaml
+++ b/swagger/ghc.yaml
@@ -5805,6 +5805,10 @@ definitions:
$ref: '#/definitions/NullableString'
grade:
$ref: '#/definitions/Grade'
+ hasDependents:
+ type: boolean
+ title: Are dependents included in your orders?
+ x-nullable: true
required:
- issueDate
- reportByDate
From 8f962330cf6352dee642a13d11b6bd9ff00fbca4 Mon Sep 17 00:00:00 2001
From: JamesHawks224 <146897935+JamesHawks224@users.noreply.github.com>
Date: Tue, 10 Dec 2024 20:31:25 +0000
Subject: [PATCH 03/37] update swagger api to return hasDependents for getOrder
---
pkg/handlers/ghcapi/internal/payloads/model_to_payload.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go
index 163b1052cda..b7167e4e504 100644
--- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go
+++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go
@@ -680,6 +680,7 @@ func Order(order *models.Order) *ghcmessages.Order {
MoveCode: moveCode,
MoveTaskOrderID: moveTaskOrderID,
OriginDutyLocationGBLOC: ghcmessages.GBLOC(swag.StringValue(order.OriginDutyLocationGBLOC)),
+ HasDependents: order.HasDependents,
}
return &payload
From bc18330e1a7218675a89df0e59f0ffe29b184c63 Mon Sep 17 00:00:00 2001
From: JamesHawks224 <146897935+JamesHawks224@users.noreply.github.com>
Date: Thu, 12 Dec 2024 16:32:50 +0000
Subject: [PATCH 04/37] fixed linter error due to missing space
---
src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx b/src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx
index e9d874690ec..e4ab0bf904c 100644
--- a/src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx
+++ b/src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx
@@ -153,7 +153,7 @@ const OrdersInfoForm = ({ ordersTypeOptions, initialValues, onSubmit, onBack })
const handleHasDependentsChange = (e) => {
// Declare a duplicate local scope of the field value
// for the form to prevent state race conditions
- if (e.target.value === '') {
+ if (e.target.value === '') {
setFieldValue('has_dependents', '');
} else {
const fieldValueHasDependents = e.target.value === 'yes';
From e6a690a5b7f0a396656bc79cd90489b96899556b Mon Sep 17 00:00:00 2001
From: JamesHawks224 <146897935+JamesHawks224@users.noreply.github.com>
Date: Thu, 12 Dec 2024 18:08:12 +0000
Subject: [PATCH 05/37] fixed hasDependents update via the api
---
.../ServicesCounselingOrders/ServicesCounselingOrders.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx
index 86fe3879045..9e205cc0b55 100644
--- a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx
+++ b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx
@@ -277,7 +277,7 @@ const ServicesCounselingOrders = ({ files, amendedDocumentId, updateAmendedDocum
reportByDate: formatSwaggerDate(values.reportByDate),
ordersType: values.ordersType,
grade: values.payGrade,
- has_dependents: formatYesNoAPIValue('no'),
+ hasDependents: formatYesNoAPIValue('no'),
};
mutateOrders({ orderID: orderId, ifMatchETag: newOrderEtag, body: orderBody });
};
From 1051f7506d828150473ae9e21124e374dbcca7dc Mon Sep 17 00:00:00 2001
From: JamesHawks224 <146897935+JamesHawks224@users.noreply.github.com>
Date: Thu, 12 Dec 2024 18:36:13 +0000
Subject: [PATCH 06/37] functional change for SC complete. tests need to be
added.
---
.../ServicesCounselingOrders.jsx | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx
index 9e205cc0b55..fb311e68928 100644
--- a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx
+++ b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx
@@ -13,7 +13,12 @@ import styles from 'styles/documentViewerWithSidebar.module.scss';
import { milmoveLogger } from 'utils/milmoveLog';
import OrdersDetailForm from 'components/Office/OrdersDetailForm/OrdersDetailForm';
import { DEPARTMENT_INDICATOR_OPTIONS } from 'constants/departmentIndicators';
-import { ORDERS_TYPE_DETAILS_OPTIONS, ORDERS_TYPE_OPTIONS, ORDERS_PAY_GRADE_OPTIONS } from 'constants/orders';
+import {
+ ORDERS_TYPE_DETAILS_OPTIONS,
+ ORDERS_TYPE_OPTIONS,
+ ORDERS_PAY_GRADE_OPTIONS,
+ ORDERS_TYPE,
+} from 'constants/orders';
import { ORDERS } from 'constants/queryKeys';
import { servicesCounselingRoutes } from 'constants/routes';
import { useOrdersDocumentQueries } from 'hooks/queries';
@@ -41,6 +46,7 @@ const ServicesCounselingOrders = ({ files, amendedDocumentId, updateAmendedDocum
const [orderTypeOptions, setOrderTypeOptions] = useState(ORDERS_TYPE_OPTIONS);
const orderId = move?.ordersId;
+ const initialValueOfHasDependents = orders[orderId]?.has_dependents;
const orderDocumentId = orders[orderId]?.uploaded_order_id;
const amendedOrderDocumentId = orders[orderId]?.uploadedAmendedOrderID || amendedDocumentId;
@@ -277,7 +283,10 @@ const ServicesCounselingOrders = ({ files, amendedDocumentId, updateAmendedDocum
reportByDate: formatSwaggerDate(values.reportByDate),
ordersType: values.ordersType,
grade: values.payGrade,
- hasDependents: formatYesNoAPIValue('no'),
+ hasDependents:
+ values.ordersType === ORDERS_TYPE.STUDENT_TRAVEL || values.ordersType === ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS
+ ? formatYesNoAPIValue('yes')
+ : initialValueOfHasDependents,
};
mutateOrders({ orderID: orderId, ifMatchETag: newOrderEtag, body: orderBody });
};
From 7b05e17cf5b5ef584241384b821b836d0d24b17d Mon Sep 17 00:00:00 2001
From: JamesHawks224 <146897935+JamesHawks224@users.noreply.github.com>
Date: Fri, 13 Dec 2024 19:55:19 +0000
Subject: [PATCH 07/37] added functionality for SC create move
---
.../Office/AddOrdersForm/AddOrdersForm.jsx | 66 +++++++++++++++----
1 file changed, 55 insertions(+), 11 deletions(-)
diff --git a/src/components/Office/AddOrdersForm/AddOrdersForm.jsx b/src/components/Office/AddOrdersForm/AddOrdersForm.jsx
index 07949b46c2e..27b1d563fc0 100644
--- a/src/components/Office/AddOrdersForm/AddOrdersForm.jsx
+++ b/src/components/Office/AddOrdersForm/AddOrdersForm.jsx
@@ -13,7 +13,7 @@ import ToolTip from 'shared/ToolTip/ToolTip';
import { DatePickerInput, DropdownInput, DutyLocationInput } from 'components/form/fields';
import { Form } from 'components/form/Form';
import SectionWrapper from 'components/Customer/SectionWrapper';
-import { ORDERS_PAY_GRADE_OPTIONS } from 'constants/orders';
+import { ORDERS_PAY_GRADE_OPTIONS, ORDERS_TYPE } from 'constants/orders';
import { dropdownInputOptions } from 'utils/formatters';
import WizardNavigation from 'components/Customer/WizardNavigation/WizardNavigation';
import Callout from 'components/Callout';
@@ -32,6 +32,9 @@ const AddOrdersForm = ({ onSubmit, ordersTypeOptions, initialValues, onBack, isS
const [hasDependents, setHasDependents] = useState(false);
const [isOconusMove, setIsOconusMove] = useState(false);
const [enableUB, setEnableUB] = useState(false);
+ const [isHasDependentsDisabled, setHasDependentsDisabled] = useState(false);
+ const [prevOrderType, setPrevOrderType] = useState('');
+ const [filteredOrderTypeOptions, setFilteredOrderTypeOptions] = useState(ordersTypeOptions);
const validationSchema = Yup.object().shape({
ordersType: Yup.mixed()
@@ -88,9 +91,24 @@ const AddOrdersForm = ({ onSubmit, ordersTypeOptions, initialValues, onBack, isS
}
}, [currentDutyLocation, newDutyLocation, isOconusMove, hasDependents, enableUB]);
+ useEffect(() => {
+ const fetchData = async () => {
+ const alaskaEnabled = await isBooleanFlagEnabled(FEATURE_FLAG_KEYS.ENABLE_ALASKA);
+
+ const updatedOptions = alaskaEnabled
+ ? ordersTypeOptions
+ : ordersTypeOptions.filter(
+ (e) => e.key !== ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS && e.key !== ORDERS_TYPE.STUDENT_TRAVEL,
+ );
+
+ setFilteredOrderTypeOptions(updatedOptions);
+ };
+ fetchData();
+ }, [ordersTypeOptions]);
+
return (
- {({ values, isValid, isSubmitting, handleSubmit, touched, setFieldValue }) => {
+ {({ values, isValid, isSubmitting, handleSubmit, handleChange, touched, setFieldValue }) => {
const isRetirementOrSeparation = ['RETIREMENT', 'SEPARATION'].includes(values.ordersType);
if (!values.origin_duty_location && touched.origin_duty_location) originMeta = 'Required';
else originMeta = null;
@@ -100,17 +118,37 @@ const AddOrdersForm = ({ onSubmit, ordersTypeOptions, initialValues, onBack, isS
const handleHasDependentsChange = (e) => {
// Declare a duplicate local scope of the field value
// for the form to prevent state race conditions
- const fieldValueHasDependents = e.target.value === 'yes';
- setHasDependents(e.target.value === 'yes');
- setFieldValue('hasDependents', fieldValueHasDependents ? 'yes' : 'no');
- if (fieldValueHasDependents && isOconusMove && enableUB) {
- setShowAccompaniedTourField(true);
- setShowDependentAgeFields(true);
+ if (e.target.value === '') {
+ setFieldValue('hasDependents', '');
} else {
- setShowAccompaniedTourField(false);
- setShowDependentAgeFields(false);
+ const fieldValueHasDependents = e.target.value === 'yes';
+ setHasDependents(e.target.value === 'yes');
+ setFieldValue('hasDependents', fieldValueHasDependents ? 'yes' : 'no');
+ if (fieldValueHasDependents && isOconusMove && enableUB) {
+ setShowAccompaniedTourField(true);
+ setShowDependentAgeFields(true);
+ } else {
+ setShowAccompaniedTourField(false);
+ setShowDependentAgeFields(false);
+ }
}
};
+ const handleOrderTypeChange = (e) => {
+ const { value } = e.target;
+ if (value === ORDERS_TYPE.STUDENT_TRAVEL || value === ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS) {
+ setHasDependentsDisabled(true);
+ handleHasDependentsChange({ target: { value: 'yes' } });
+ } else {
+ setHasDependentsDisabled(false);
+ if (
+ prevOrderType === ORDERS_TYPE.STUDENT_TRAVEL ||
+ prevOrderType === ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS
+ ) {
+ handleHasDependentsChange({ target: { value: '' } });
+ }
+ }
+ setPrevOrderType(value);
+ };
return (
From d4e41949b98d51c929e5e9bd0cb9893b47f0c853 Mon Sep 17 00:00:00 2001
From: KonstanceH
Date: Wed, 18 Dec 2024 21:04:53 +0000
Subject: [PATCH 08/37] add int changes and generate new migration file
---
migrations/app/migrations_manifest.txt | 1 +
...add_international_nts_service_items.up.sql | 21 ++++
.../mto_shipment/mto_shipment_updater.go | 16 +--
.../mto_shipment/mto_shipment_updater_test.go | 105 ++++++++++++++++++
.../mto_shipment/shipment_approver.go | 19 ++--
.../mto_shipment/shipment_approver_test.go | 103 ++++++++++++++++-
6 files changed, 249 insertions(+), 16 deletions(-)
create mode 100644 migrations/app/schema/20241218204620_add_international_nts_service_items.up.sql
diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt
index 3f453bf1226..0b91d4bfe2a 100644
--- a/migrations/app/migrations_manifest.txt
+++ b/migrations/app/migrations_manifest.txt
@@ -1051,3 +1051,4 @@
20241204155919_update_ordering_proc.up.sql
20241204210208_retroactive_update_of_ppm_max_and_estimated_incentives_prd.up.sql
20241216170325_update_nts_enum_name.up.sql
+20241218204620_add_international_nts_service_items.up.sql
diff --git a/migrations/app/schema/20241218204620_add_international_nts_service_items.up.sql b/migrations/app/schema/20241218204620_add_international_nts_service_items.up.sql
new file mode 100644
index 00000000000..2bb5207813f
--- /dev/null
+++ b/migrations/app/schema/20241218204620_add_international_nts_service_items.up.sql
@@ -0,0 +1,21 @@
+--
+-- Add service items for international NTS shipments.
+--
+INSERT INTO re_service_items
+(id, service_id, shipment_type, market_code, is_auto_approved, created_at, updated_at, sort)
+VALUES
+ ('2a560507-db09-4be1-b809-49c0f515b31b', '9f3d551a-0725-430e-897e-80ee9add3ae9' ,'HHG_INTO_NTS', 'i', true, now(), now(), 1),
+ ('e702818f-defd-452c-81a3-865b902e7dd0', '388115e8-abe9-441d-96cf-a39f24baa0a3' ,'HHG_INTO_NTS', 'i', true, now(), now(), 2),
+ ('366ee5a4-eb61-4ded-a68c-ddc29fe1a886', '874cb86a-bc39-4f57-a614-53ee3fcacf14' ,'HHG_INTO_NTS', 'i', true, now(), now(), 3),
+ ('aac4e95e-27ed-4f09-9b6b-384d8542f410', '86203d72-7f7c-49ff-82f0-5b95e4958f60' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ ('010f2f91-7381-4149-8d74-8eb5f593a864', '806c6d59-57ff-4a3f-9518-ebf29ba9cb10' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ ('a41966b7-b83a-4eaf-8e68-d5e884777102', '28389ee1-56cf-400c-aa52-1501ecdd7c69' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ ('14c77957-3c76-465a-bb07-c98d36ef1e54', 'bd6064ca-e780-4ab4-a37b-0ae98eebb244' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ ('d52d2d03-100a-4ed9-b2de-16eac63a375f', '22fc07ed-be15-4f50-b941-cbd38153b378' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ ('7fd91408-7d69-4375-b7e6-5b2ff714206b', 'bd424e45-397b-4766-9712-de4ae3a2da36' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ ('b3dc509d-d652-4300-a702-a1ddce6255b6', 'b488bf85-ea5e-49c8-ba5c-e2fa278ac806' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ ('001eadb6-3526-45b9-96e0-0648bb481e86', '6f4f6e31-0675-4051-b659-89832259f390' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ ('b991c5c9-af2c-4146-b999-1d0bdf91de3f', '624a97c5-dfbf-4da9-a6e9-526b4f95af8d' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ ('5a89315a-257b-4ef0-92cb-4c06aa6f1332', '4132416b-b1aa-42e7-98f2-0ac0a03e8a31' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ ('d4a98dea-a5f7-4b92-b5de-e6350ab07824', '81e29d0c-02a6-4a7a-be02-554deb3ee49e' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ ('eaea90c2-93d3-4db9-89cd-23ac57ec9ce1', '690a5fc1-0ea5-4554-8294-a367b5daefa9' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL);
diff --git a/pkg/services/mto_shipment/mto_shipment_updater.go b/pkg/services/mto_shipment/mto_shipment_updater.go
index 21b54188170..1c67d6f578a 100644
--- a/pkg/services/mto_shipment/mto_shipment_updater.go
+++ b/pkg/services/mto_shipment/mto_shipment_updater.go
@@ -1092,13 +1092,15 @@ func reServiceCodesForShipment(shipment models.MTOShipment) []models.ReServiceCo
}
}
case models.MTOShipmentTypeHHGIntoNTS:
- // Need to create: Dom Linehaul, Fuel Surcharge, Dom Origin Price, Dom Destination Price, Dom NTS Packing
- return []models.ReServiceCode{
- models.ReServiceCodeDLH,
- models.ReServiceCodeFSC,
- models.ReServiceCodeDOP,
- models.ReServiceCodeDDP,
- models.ReServiceCodeDNPK,
+ if shipment.MarketCode != models.MarketCodeInternational {
+ // Need to create: Dom Linehaul, Fuel Surcharge, Dom Origin Price, Dom Destination Price, Dom NTS Packing
+ return []models.ReServiceCode{
+ models.ReServiceCodeDLH,
+ models.ReServiceCodeFSC,
+ models.ReServiceCodeDOP,
+ models.ReServiceCodeDDP,
+ models.ReServiceCodeDNPK,
+ }
}
case models.MTOShipmentTypeHHGOutOfNTSDom:
// Need to create: Dom Linehaul, Fuel Surcharge, Dom Origin Price, Dom Destination Price, Dom Unpacking
diff --git a/pkg/services/mto_shipment/mto_shipment_updater_test.go b/pkg/services/mto_shipment/mto_shipment_updater_test.go
index 185319eb3d7..7b66c7417d3 100644
--- a/pkg/services/mto_shipment/mto_shipment_updater_test.go
+++ b/pkg/services/mto_shipment/mto_shipment_updater_test.go
@@ -3031,3 +3031,108 @@ func (suite *MTOShipmentServiceSuite) TestUpdateStatusServiceItems() {
suite.Equal(models.ReServiceCodeDSH, serviceItems[0].ReService.Code)
})
}
+
+func (suite *MTOShipmentServiceSuite) TestUpdateDomesticServiceItems() {
+
+ expectedReServiceCodes := []models.ReServiceCode{
+ models.ReServiceCodeDLH,
+ models.ReServiceCodeFSC,
+ models.ReServiceCodeDOP,
+ models.ReServiceCodeDDP,
+ models.ReServiceCodeDNPK,
+ }
+
+ var pickupAddress models.Address
+ var storageFacility models.StorageFacility
+ var mto models.Move
+
+ setupTestData := func() {
+ for i := range expectedReServiceCodes {
+ factory.FetchReServiceByCode(suite.DB(), expectedReServiceCodes[i])
+ }
+
+ pickupAddress = factory.BuildAddress(suite.DB(), []factory.Customization{
+ {
+ Model: models.Address{
+ StreetAddress1: "Test Street 1",
+ City: "Des moines",
+ State: "IA",
+ PostalCode: "50309",
+ IsOconus: models.BoolPointer(false),
+ },
+ },
+ }, nil)
+
+ storageFacility = factory.BuildStorageFacility(suite.DB(), []factory.Customization{
+ {
+ Model: models.Address{
+ StreetAddress1: "Test Street Adress 2",
+ City: "Des moines",
+ State: "IA",
+ PostalCode: "50314",
+ IsOconus: models.BoolPointer(false),
+ },
+ },
+ }, nil)
+
+ mto = factory.BuildMove(suite.DB(), []factory.Customization{
+ {
+ Model: models.Move{
+ Status: models.MoveStatusAPPROVED,
+ },
+ },
+ }, nil)
+ }
+
+ builder := query.NewQueryBuilder()
+ moveRouter := moveservices.NewMoveRouter()
+ planner := &mocks.Planner{}
+ planner.On("ZipTransitDistance",
+ mock.AnythingOfType("*appcontext.appContext"),
+ mock.Anything,
+ mock.Anything,
+ ).Return(400, nil)
+ siCreator := mtoserviceitem.NewMTOServiceItemCreator(planner, builder, moveRouter, ghcrateengine.NewDomesticUnpackPricer(), ghcrateengine.NewDomesticPackPricer(), ghcrateengine.NewDomesticLinehaulPricer(), ghcrateengine.NewDomesticShorthaulPricer(), ghcrateengine.NewDomesticOriginPricer(), ghcrateengine.NewDomesticDestinationPricer(), ghcrateengine.NewFuelSurchargePricer())
+ updater := NewMTOShipmentStatusUpdater(builder, siCreator, planner)
+
+ suite.Run("Preapproved service items successfully added to domestic nts shipments", func() {
+ setupTestData()
+
+ shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{
+ {
+ Model: mto,
+ LinkOnly: true,
+ },
+ {
+ Model: pickupAddress,
+ Type: &factory.Addresses.PickupAddress,
+ LinkOnly: true,
+ },
+ {
+ Model: storageFacility,
+ Type: &factory.StorageFacility,
+ LinkOnly: true,
+ },
+ {
+ Model: models.MTOShipment{
+ ShipmentType: models.MTOShipmentTypeHHGIntoNTS,
+ Status: models.MTOShipmentStatusSubmitted,
+ },
+ },
+ }, nil)
+
+ appCtx := suite.AppContextForTest()
+ eTag := etag.GenerateEtag(shipment.UpdatedAt)
+
+ updatedShipment, err := updater.UpdateMTOShipmentStatus(appCtx, shipment.ID, models.MTOShipmentStatusApproved, nil, nil, eTag)
+ suite.NoError(err)
+
+ serviceItems := models.MTOServiceItems{}
+ err = appCtx.DB().EagerPreload("ReService").Where("mto_shipment_id = ?", updatedShipment.ID).All(&serviceItems)
+ suite.NoError(err)
+
+ for i := 0; i < len(expectedReServiceCodes); i++ {
+ suite.Equal(expectedReServiceCodes[i], serviceItems[i].ReService.Code)
+ }
+ })
+}
diff --git a/pkg/services/mto_shipment/shipment_approver.go b/pkg/services/mto_shipment/shipment_approver.go
index d52d7388ec4..5e05cec2f2f 100644
--- a/pkg/services/mto_shipment/shipment_approver.go
+++ b/pkg/services/mto_shipment/shipment_approver.go
@@ -2,6 +2,7 @@ package mtoshipment
import (
"math"
+ "slices"
"github.com/gofrs/uuid"
"github.com/pkg/errors"
@@ -77,14 +78,18 @@ func (f *shipmentApprover) ApproveShipment(appCtx appcontext.AppContext, shipmen
}
}
- // create international shipment service items
- if shipment.ShipmentType == models.MTOShipmentTypeHHG && shipment.MarketCode == models.MarketCodeInternational {
- err := models.CreateApprovedServiceItemsForShipment(appCtx.DB(), shipment)
- if err != nil {
- return shipment, err
- }
- }
transactionError := appCtx.NewTransaction(func(txnAppCtx appcontext.AppContext) error {
+
+ // If there are existing 're_service_items' for the international shipment then create 'mto_service_items'
+ // These are currently the shipment types we handle but add any additional international shipment types here
+ internationalShipmentTypes := []models.MTOShipmentType{models.MTOShipmentTypeHHG, models.MTOShipmentTypeHHGIntoNTS}
+ if slices.Contains(internationalShipmentTypes, shipment.ShipmentType) && shipment.MarketCode == models.MarketCodeInternational {
+ err := models.CreateApprovedServiceItemsForShipment(appCtx.DB(), shipment)
+ if err != nil {
+ return err
+ }
+ }
+
verrs, err := txnAppCtx.DB().ValidateAndSave(shipment)
if verrs != nil && verrs.HasAny() {
invalidInputError := apperror.NewInvalidInputError(shipment.ID, nil, verrs, "There was an issue with validating the updates")
diff --git a/pkg/services/mto_shipment/shipment_approver_test.go b/pkg/services/mto_shipment/shipment_approver_test.go
index 3c1d36a4795..7033031584f 100644
--- a/pkg/services/mto_shipment/shipment_approver_test.go
+++ b/pkg/services/mto_shipment/shipment_approver_test.go
@@ -190,7 +190,7 @@ func (suite *MTOShipmentServiceSuite) createApproveShipmentSubtestData() (subtes
}
func (suite *MTOShipmentServiceSuite) TestApproveShipment() {
- suite.Run("If the international mtoShipment is approved successfully it should create pre approved mtoServiceItems", func() {
+ suite.Run("Given international mtoShipment is approved successfully pre-approved mtoServiceItems are created HHG", func() {
internationalShipment := factory.BuildMTOShipment(suite.AppContextForTest().DB(), []factory.Customization{
{
Model: models.Move{
@@ -209,7 +209,7 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() {
},
{
Model: models.MTOShipment{
- MarketCode: "i",
+ MarketCode: models.MarketCodeInternational,
Status: models.MTOShipmentStatusSubmitted,
},
},
@@ -255,6 +255,84 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() {
}
})
+ suite.Run("Given international mtoShipment is approved successfully pre-approved mtoServiceItems are created NTS", func() {
+ storageFacility := factory.BuildStorageFacility(suite.DB(), []factory.Customization{
+ {
+ Model: models.StorageFacility{
+ FacilityName: *models.StringPointer("Test Storage Name"),
+ Email: models.StringPointer("old@email.com"),
+ LotNumber: models.StringPointer("Test lot number"),
+ Phone: models.StringPointer("555-555-5555"),
+ },
+ },
+ {
+ Model: models.Address{
+ StreetAddress1: "JBER",
+ City: "Anchorage",
+ State: "AK",
+ PostalCode: "99507",
+ IsOconus: models.BoolPointer(true),
+ },
+ },
+ }, nil)
+
+ internationalShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{
+ {
+ Model: models.Move{
+ Status: models.MoveStatusAPPROVED,
+ },
+ },
+ {
+ Model: models.Address{
+ StreetAddress1: "Tester Address",
+ City: "Des Moines",
+ State: "IA",
+ PostalCode: "50314",
+ IsOconus: models.BoolPointer(false),
+ },
+ Type: &factory.Addresses.PickupAddress,
+ },
+ {
+ Model: models.MTOShipment{
+ MarketCode: models.MarketCodeInternational,
+ Status: models.MTOShipmentStatusSubmitted,
+ ShipmentType: models.MTOShipmentTypeHHGIntoNTS,
+ },
+ },
+ {
+ Model: storageFacility,
+ LinkOnly: true,
+ },
+ }, nil)
+ internationalShipmentEtag := etag.GenerateEtag(internationalShipment.UpdatedAt)
+
+ shipmentRouter := NewShipmentRouter()
+ var serviceItemCreator services.MTOServiceItemCreator
+ var planner route.Planner
+ var moveWeights services.MoveWeights
+
+ // Approve international shipment
+ shipmentApprover := NewShipmentApprover(shipmentRouter, serviceItemCreator, planner, moveWeights)
+ _, err := shipmentApprover.ApproveShipment(suite.AppContextForTest(), internationalShipment.ID, internationalShipmentEtag)
+ suite.NoError(err)
+
+ // Get created pre approved service items
+ var serviceItems []models.MTOServiceItem
+ err2 := suite.AppContextForTest().DB().EagerPreload("ReService").Where("mto_shipment_id = ?", internationalShipment.ID).Order("created_at asc").All(&serviceItems)
+ suite.NoError(err2)
+
+ expectedReserviceCodes := []models.ReServiceCode{
+ models.ReServiceCodeISLH,
+ models.ReServiceCodeINPK,
+ }
+
+ suite.Equal(len(expectedReserviceCodes), len(serviceItems))
+ for i := 0; i < len(serviceItems); i++ {
+ actualReServiceCode := serviceItems[i].ReService.Code
+ suite.True(slices.Contains(expectedReserviceCodes, actualReServiceCode))
+ }
+ })
+
suite.Run("If the mtoShipment is approved successfully it should create approved mtoServiceItems", func() {
subtestData := suite.createApproveShipmentSubtestData()
appCtx := subtestData.appCtx
@@ -787,4 +865,25 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() {
suite.NotNil(shipment.MoveTaskOrder.ExcessWeightQualifiedAt)
})
+
+ suite.Run("Given invalid shipment error returned", func() {
+ invalidShipment := factory.BuildMTOShipment(suite.AppContextForTest().DB(), []factory.Customization{
+ {
+ Model: models.MTOShipment{
+ ShipmentType: models.MTOShipmentTypePPM,
+ },
+ },
+ }, nil)
+ invalidShipmentEtag := etag.GenerateEtag(invalidShipment.UpdatedAt)
+
+ shipmentRouter := NewShipmentRouter()
+ var serviceItemCreator services.MTOServiceItemCreator
+ var planner route.Planner
+ var moveWeights services.MoveWeights
+
+ // Approve international shipment
+ shipmentApprover := NewShipmentApprover(shipmentRouter, serviceItemCreator, planner, moveWeights)
+ _, err := shipmentApprover.ApproveShipment(suite.AppContextForTest(), invalidShipment.ID, invalidShipmentEtag)
+ suite.Error(err)
+ })
}
From 03eb1289f1473693074899640838d88c788317e2 Mon Sep 17 00:00:00 2001
From: KonstanceH
Date: Thu, 19 Dec 2024 14:17:53 +0000
Subject: [PATCH 09/37] adding comments for re_services
---
...620_add_international_nts_service_items.up.sql | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/migrations/app/schema/20241218204620_add_international_nts_service_items.up.sql b/migrations/app/schema/20241218204620_add_international_nts_service_items.up.sql
index 2bb5207813f..207c5379080 100644
--- a/migrations/app/schema/20241218204620_add_international_nts_service_items.up.sql
+++ b/migrations/app/schema/20241218204620_add_international_nts_service_items.up.sql
@@ -4,18 +4,33 @@
INSERT INTO re_service_items
(id, service_id, shipment_type, market_code, is_auto_approved, created_at, updated_at, sort)
VALUES
+ --ISLH International Shipping & Linehaul
('2a560507-db09-4be1-b809-49c0f515b31b', '9f3d551a-0725-430e-897e-80ee9add3ae9' ,'HHG_INTO_NTS', 'i', true, now(), now(), 1),
+ --PODFSC International POD Fuel Surcharge
('e702818f-defd-452c-81a3-865b902e7dd0', '388115e8-abe9-441d-96cf-a39f24baa0a3' ,'HHG_INTO_NTS', 'i', true, now(), now(), 2),
+ --INPK International NTS packing
('366ee5a4-eb61-4ded-a68c-ddc29fe1a886', '874cb86a-bc39-4f57-a614-53ee3fcacf14' ,'HHG_INTO_NTS', 'i', true, now(), now(), 3),
+ --ICRT International crating
('aac4e95e-27ed-4f09-9b6b-384d8542f410', '86203d72-7f7c-49ff-82f0-5b95e4958f60' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ --IDASIT International destination add'l day SIT
('010f2f91-7381-4149-8d74-8eb5f593a864', '806c6d59-57ff-4a3f-9518-ebf29ba9cb10' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ --IDDSIT International destination SIT delivery
('a41966b7-b83a-4eaf-8e68-d5e884777102', '28389ee1-56cf-400c-aa52-1501ecdd7c69' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ --IDFSIT International destination 1st day SIT
('14c77957-3c76-465a-bb07-c98d36ef1e54', 'bd6064ca-e780-4ab4-a37b-0ae98eebb244' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ --IDSHUT International destination shuttle service
('d52d2d03-100a-4ed9-b2de-16eac63a375f', '22fc07ed-be15-4f50-b941-cbd38153b378' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ --IOASIT International origin add'l day SIT
('7fd91408-7d69-4375-b7e6-5b2ff714206b', 'bd424e45-397b-4766-9712-de4ae3a2da36' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ --IOFSIT International origin 1st day SIT
('b3dc509d-d652-4300-a702-a1ddce6255b6', 'b488bf85-ea5e-49c8-ba5c-e2fa278ac806' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ --IOPSIT International origin SIT pickup
('001eadb6-3526-45b9-96e0-0648bb481e86', '6f4f6e31-0675-4051-b659-89832259f390' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ --IOSHUT International origin shuttle service
('b991c5c9-af2c-4146-b999-1d0bdf91de3f', '624a97c5-dfbf-4da9-a6e9-526b4f95af8d' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ --IUCRT International uncrating
('5a89315a-257b-4ef0-92cb-4c06aa6f1332', '4132416b-b1aa-42e7-98f2-0ac0a03e8a31' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ --IOFSC International Origin SIT Fuel Surcharge
('d4a98dea-a5f7-4b92-b5de-e6350ab07824', '81e29d0c-02a6-4a7a-be02-554deb3ee49e' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL),
+ --IDSFSC International Destination SIT Fuel Surcharge
('eaea90c2-93d3-4db9-89cd-23ac57ec9ce1', '690a5fc1-0ea5-4554-8294-a367b5daefa9' ,'HHG_INTO_NTS', 'i', false, now(), now(), NULL);
From a30890a8575deb5fa0879f559a4030c942868588 Mon Sep 17 00:00:00 2001
From: JamesHawks224 <146897935+JamesHawks224@users.noreply.github.com>
Date: Thu, 19 Dec 2024 17:01:45 +0000
Subject: [PATCH 10/37] added tests for SC edit orders page
---
.../ServicesCounselingOrders.test.jsx | 348 ++++++++++++++++++
1 file changed, 348 insertions(+)
diff --git a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.test.jsx b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.test.jsx
index c6d994b9a84..b10032c6da9 100644
--- a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.test.jsx
+++ b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.test.jsx
@@ -7,6 +7,9 @@ import ServicesCounselingOrders from 'pages/Office/ServicesCounselingOrders/Serv
import { MockProviders } from 'testUtils';
import { useOrdersDocumentQueries } from 'hooks/queries';
import { MOVE_DOCUMENT_TYPE } from 'shared/constants';
+import { counselingUpdateOrder, getOrder } from 'services/ghcApi';
+import { formatYesNoAPIValue } from 'utils/formatters';
+import { ORDERS_TYPE } from 'constants/orders';
const mockOriginDutyLocation = {
address: {
@@ -100,6 +103,13 @@ jest.mock('services/ghcApi', () => ({
// Default to no LOA
return Promise.resolve(undefined);
},
+ counselingUpdateOrder: jest.fn(),
+ getOrder: jest.fn(),
+}));
+
+jest.mock('utils/featureFlags', () => ({
+ ...jest.requireActual('utils/featureFlags'),
+ isBooleanFlagEnabled: jest.fn().mockImplementation(() => Promise.resolve(true)),
}));
const useOrdersDocumentQueriesReturnValue = {
@@ -218,6 +228,8 @@ describe('Orders page', () => {
});
it('renders each option for orders type dropdown', async () => {
+ useOrdersDocumentQueries.mockReturnValue(useOrdersDocumentQueriesReturnValue);
+
render(
@@ -238,6 +250,12 @@ describe('Orders page', () => {
await userEvent.selectOptions(ordersTypeDropdown, 'SEPARATION');
expect(ordersTypeDropdown).toHaveValue('SEPARATION');
+
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS);
+ expect(ordersTypeDropdown).toHaveValue(ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS);
+
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.STUDENT_TRAVEL);
+ expect(ordersTypeDropdown).toHaveValue(ORDERS_TYPE.STUDENT_TRAVEL);
});
it('populates initial field values', async () => {
@@ -409,6 +427,7 @@ describe('Orders page', () => {
});
});
});
+
describe('LOA concatenation', () => {
it('concatenates the LOA string correctly', async () => {
useOrdersDocumentQueries.mockReturnValue(useOrdersDocumentQueriesReturnValue);
@@ -430,6 +449,7 @@ describe('Orders page', () => {
expect(loaTextField).toHaveValue(expectedLongLineOfAccounting);
});
});
+
describe('LOA concatenation with regex removes extra spaces', () => {
it('concatenates the LOA string correctly and without extra spaces', async () => {
let extraSpacesLongLineOfAccounting =
@@ -446,4 +466,332 @@ describe('Orders page', () => {
expect(extraSpacesLongLineOfAccounting).toEqual(expectedLongLineOfAccounting);
});
});
+
+ describe('Order type: STUDENT_TRAVEL', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('select STUDENT_TRAVEL', async () => {
+ // create a local copy of order return value and set initial values
+ const orderQueryReturnValues = JSON.parse(JSON.stringify(useOrdersDocumentQueriesReturnValue));
+ orderQueryReturnValues.move = { id: 123, moveCode: 'GLOBAL123', ordersId: 1 };
+ orderQueryReturnValues.orders[1].order_type = ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION;
+ orderQueryReturnValues.orders[1].has_dependents = formatYesNoAPIValue('no');
+
+ // set return values for mocked functions
+ useOrdersDocumentQueries.mockReturnValue(orderQueryReturnValues);
+ getOrder.mockResolvedValue(orderQueryReturnValues);
+
+ // render component
+ render(
+
+
+ ,
+ );
+
+ // Select STUDENT_TRAVEL from the dropdown
+ const ordersTypeDropdown = await screen.findByLabelText('Orders type');
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.STUDENT_TRAVEL);
+
+ // Submit the form
+ const saveButton = screen.getByRole('button', { name: 'Save' });
+ await userEvent.click(saveButton);
+
+ // Verify correct values were passed
+ await waitFor(() => {
+ expect(counselingUpdateOrder).toHaveBeenCalledWith(
+ expect.objectContaining({
+ body: expect.objectContaining({
+ hasDependents: formatYesNoAPIValue('yes'),
+ ordersType: ORDERS_TYPE.STUDENT_TRAVEL,
+ }),
+ }),
+ );
+ });
+ });
+
+ it('De-select STUDENT_TRAVEL', async () => {
+ // create a local copy of order return value and set initial values
+ const orderQueryReturnValues = JSON.parse(JSON.stringify(useOrdersDocumentQueriesReturnValue));
+ orderQueryReturnValues.move = { id: 123, moveCode: 'GLOBAL123', ordersId: 1 };
+ orderQueryReturnValues.orders[1].order_type = ORDERS_TYPE.STUDENT_TRAVEL;
+ orderQueryReturnValues.orders[1].has_dependents = formatYesNoAPIValue('yes');
+
+ // set return values for mocked functions
+ useOrdersDocumentQueries.mockReturnValue(orderQueryReturnValues);
+ getOrder.mockResolvedValue(orderQueryReturnValues);
+
+ // render component
+ render(
+
+
+ ,
+ );
+
+ // De-select STUDENT_TRAVEL from the dropdown
+ const ordersTypeDropdown = await screen.findByLabelText('Orders type');
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION);
+
+ // Submit the form
+ const saveButton = screen.getByRole('button', { name: 'Save' });
+ await userEvent.click(saveButton);
+
+ // Verify correct values were passed
+ await waitFor(() => {
+ expect(counselingUpdateOrder).toHaveBeenCalledWith(
+ expect.objectContaining({
+ body: expect.objectContaining({
+ hasDependents: formatYesNoAPIValue('yes'),
+ ordersType: ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION,
+ }),
+ }),
+ );
+ });
+ });
+
+ it('select STUDENT_TRAVEL, De-select STUDENT_TRAVEL', async () => {
+ // create a local copy of order return value and set initial values
+ const orderQueryReturnValues = JSON.parse(JSON.stringify(useOrdersDocumentQueriesReturnValue));
+ orderQueryReturnValues.move = { id: 123, moveCode: 'GLOBAL123', ordersId: 1 };
+ orderQueryReturnValues.orders[1].order_type = ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION;
+ orderQueryReturnValues.orders[1].has_dependents = formatYesNoAPIValue('no');
+
+ // set return values for mocked functions
+ useOrdersDocumentQueries.mockReturnValue(orderQueryReturnValues);
+ getOrder.mockResolvedValue(orderQueryReturnValues);
+
+ // render component
+ render(
+
+
+ ,
+ );
+
+ // Select EARLY_RETURN_OF_DEPENDENTS and then de-select from the dropdown
+ const ordersTypeDropdown = await screen.findByLabelText('Orders type');
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.STUDENT_TRAVEL);
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.LOCAL_MOVE);
+
+ // Submit the form
+ const saveButton = screen.getByRole('button', { name: 'Save' });
+ await userEvent.click(saveButton);
+
+ // Verify correct values were passed
+ await waitFor(() => {
+ expect(counselingUpdateOrder).toHaveBeenCalledWith(
+ expect.objectContaining({
+ body: expect.objectContaining({
+ hasDependents: formatYesNoAPIValue('no'),
+ ordersType: ORDERS_TYPE.LOCAL_MOVE,
+ }),
+ }),
+ );
+ });
+ });
+
+ it('select STUDENT_TRAVEL, select EARLY_RETURN_OF_DEPENDENTS', async () => {
+ // create a local copy of order return value and set initial values
+ const orderQueryReturnValues = JSON.parse(JSON.stringify(useOrdersDocumentQueriesReturnValue));
+ orderQueryReturnValues.move = { id: 123, moveCode: 'GLOBAL123', ordersId: 1 };
+ orderQueryReturnValues.orders[1].order_type = ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION;
+ orderQueryReturnValues.orders[1].has_dependents = formatYesNoAPIValue('no');
+
+ // set return values for mocked functions
+ useOrdersDocumentQueries.mockReturnValue(orderQueryReturnValues);
+ getOrder.mockResolvedValue(orderQueryReturnValues);
+
+ // render component
+ render(
+
+
+ ,
+ );
+
+ // Select STUDENT_TRAVEL and then select EARLY_RETURN_OF_DEPENDENTS from the dropdown
+ const ordersTypeDropdown = await screen.findByLabelText('Orders type');
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.STUDENT_TRAVEL);
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS);
+
+ // Submit the form
+ const saveButton = screen.getByRole('button', { name: 'Save' });
+ await userEvent.click(saveButton);
+
+ // Verify correct values were passed
+ await waitFor(() => {
+ expect(counselingUpdateOrder).toHaveBeenCalledWith(
+ expect.objectContaining({
+ body: expect.objectContaining({
+ hasDependents: formatYesNoAPIValue('yes'),
+ ordersType: ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS,
+ }),
+ }),
+ );
+ });
+ });
+ });
+
+ describe('Order type: EARLY_RETURN_OF_DEPENDENTS', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('select EARLY_RETURN_OF_DEPENDENTS', async () => {
+ // create a local copy of order return value and set initial values
+ const orderQueryReturnValues = JSON.parse(JSON.stringify(useOrdersDocumentQueriesReturnValue));
+ orderQueryReturnValues.move = { id: 123, moveCode: 'GLOBAL123', ordersId: 1 };
+ orderQueryReturnValues.orders[1].order_type = ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION;
+ orderQueryReturnValues.orders[1].has_dependents = formatYesNoAPIValue('no');
+
+ // set return values for mocked functions
+ useOrdersDocumentQueries.mockReturnValue(orderQueryReturnValues);
+ getOrder.mockResolvedValue(orderQueryReturnValues);
+
+ // render component
+ render(
+
+
+ ,
+ );
+
+ // Select EARLY_RETURN_OF_DEPENDENTS from the dropdown
+ const ordersTypeDropdown = await screen.findByLabelText('Orders type');
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS);
+
+ // Submit the form
+ const saveButton = screen.getByRole('button', { name: 'Save' });
+ await userEvent.click(saveButton);
+
+ // Verify correct values were passed
+ await waitFor(() => {
+ expect(counselingUpdateOrder).toHaveBeenCalledWith(
+ expect.objectContaining({
+ body: expect.objectContaining({
+ hasDependents: formatYesNoAPIValue('yes'),
+ ordersType: ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS,
+ }),
+ }),
+ );
+ });
+ });
+
+ it('De-select EARLY_RETURN_OF_DEPENDENTS', async () => {
+ // create a local copy of order return value and set initial values
+ const orderQueryReturnValues = JSON.parse(JSON.stringify(useOrdersDocumentQueriesReturnValue));
+ orderQueryReturnValues.move = { id: 123, moveCode: 'GLOBAL123', ordersId: 1 };
+ orderQueryReturnValues.orders[1].order_type = ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS;
+ orderQueryReturnValues.orders[1].has_dependents = formatYesNoAPIValue('yes');
+
+ // set return values for mocked functions
+ useOrdersDocumentQueries.mockReturnValue(orderQueryReturnValues);
+ getOrder.mockResolvedValue(orderQueryReturnValues);
+
+ // render component
+ render(
+
+
+ ,
+ );
+
+ // De-select EARLY_RETURN_OF_DEPENDENTS from the dropdown
+ const ordersTypeDropdown = await screen.findByLabelText('Orders type');
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION);
+
+ // Submit the form
+ const saveButton = screen.getByRole('button', { name: 'Save' });
+ await userEvent.click(saveButton);
+
+ // Verify correct values were passed
+ await waitFor(() => {
+ expect(counselingUpdateOrder).toHaveBeenCalledWith(
+ expect.objectContaining({
+ body: expect.objectContaining({
+ hasDependents: formatYesNoAPIValue('yes'),
+ ordersType: ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION,
+ }),
+ }),
+ );
+ });
+ });
+
+ it('select EARLY_RETURN_OF_DEPENDENTS, De-select EARLY_RETURN_OF_DEPENDENTS', async () => {
+ // create a local copy of order return value and set initial values
+ const orderQueryReturnValues = JSON.parse(JSON.stringify(useOrdersDocumentQueriesReturnValue));
+ orderQueryReturnValues.move = { id: 123, moveCode: 'GLOBAL123', ordersId: 1 };
+ orderQueryReturnValues.orders[1].order_type = ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION;
+ orderQueryReturnValues.orders[1].has_dependents = formatYesNoAPIValue('no');
+
+ // set return values for mocked functions
+ useOrdersDocumentQueries.mockReturnValue(orderQueryReturnValues);
+ getOrder.mockResolvedValue(orderQueryReturnValues);
+
+ // render component
+ render(
+
+
+ ,
+ );
+
+ // Select EARLY_RETURN_OF_DEPENDENTS and then de-select from the dropdown
+ const ordersTypeDropdown = await screen.findByLabelText('Orders type');
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS);
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.LOCAL_MOVE);
+
+ // Submit the form
+ const saveButton = screen.getByRole('button', { name: 'Save' });
+ await userEvent.click(saveButton);
+
+ // Verify correct values were passed
+ await waitFor(() => {
+ expect(counselingUpdateOrder).toHaveBeenCalledWith(
+ expect.objectContaining({
+ body: expect.objectContaining({
+ hasDependents: formatYesNoAPIValue('no'),
+ ordersType: ORDERS_TYPE.LOCAL_MOVE,
+ }),
+ }),
+ );
+ });
+ });
+
+ it('select EARLY_RETURN_OF_DEPENDENTS, select STUDENT_TRAVEL', async () => {
+ // create a local copy of order return value and set initial values
+ const orderQueryReturnValues = JSON.parse(JSON.stringify(useOrdersDocumentQueriesReturnValue));
+ orderQueryReturnValues.move = { id: 123, moveCode: 'GLOBAL123', ordersId: 1 };
+ orderQueryReturnValues.orders[1].order_type = ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION;
+ orderQueryReturnValues.orders[1].has_dependents = formatYesNoAPIValue('no');
+
+ // set return values for mocked functions
+ useOrdersDocumentQueries.mockReturnValue(orderQueryReturnValues);
+ getOrder.mockResolvedValue(orderQueryReturnValues);
+
+ // render component
+ render(
+
+
+ ,
+ );
+
+ // Select EARLY_RETURN_OF_DEPENDENTS and then select STUDENT_TRAVEL from the dropdown
+ const ordersTypeDropdown = await screen.findByLabelText('Orders type');
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS);
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.STUDENT_TRAVEL);
+
+ // Submit the form
+ const saveButton = screen.getByRole('button', { name: 'Save' });
+ await userEvent.click(saveButton);
+
+ // Verify correct values were passed
+ await waitFor(() => {
+ expect(counselingUpdateOrder).toHaveBeenCalledWith(
+ expect.objectContaining({
+ body: expect.objectContaining({
+ hasDependents: formatYesNoAPIValue('yes'),
+ ordersType: ORDERS_TYPE.STUDENT_TRAVEL,
+ }),
+ }),
+ );
+ });
+ });
+ });
});
From de0976e71d320afdfe58ce22491b7cd1a344ae74 Mon Sep 17 00:00:00 2001
From: JamesHawks224 <146897935+JamesHawks224@users.noreply.github.com>
Date: Thu, 19 Dec 2024 18:38:08 +0000
Subject: [PATCH 11/37] add tests for add orders form
---
.../AddOrdersForm/AddOrdersForm.test.jsx | 155 +++++++++++++++++-
1 file changed, 154 insertions(+), 1 deletion(-)
diff --git a/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx b/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx
index f6c4f04e22f..43d57b49e46 100644
--- a/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx
+++ b/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx
@@ -6,7 +6,7 @@ import { Provider } from 'react-redux';
import AddOrdersForm from './AddOrdersForm';
import { dropdownInputOptions } from 'utils/formatters';
-import { ORDERS_TYPE_OPTIONS } from 'constants/orders';
+import { ORDERS_TYPE, ORDERS_TYPE_OPTIONS } from 'constants/orders';
import { configureStore } from 'shared/store';
import { isBooleanFlagEnabled } from 'utils/featureFlags';
@@ -126,6 +126,40 @@ describe('CreateMoveCustomerInfo Component', () => {
});
});
+ it('renders each option for orders type', async () => {
+ isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true));
+
+ const { getByLabelText } = render(
+
+
+ ,
+ );
+
+ const ordersTypeDropdown = getByLabelText('Orders type');
+ expect(ordersTypeDropdown).toBeInstanceOf(HTMLSelectElement);
+
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION);
+ expect(ordersTypeDropdown).toHaveValue(ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION);
+
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.LOCAL_MOVE);
+ expect(ordersTypeDropdown).toHaveValue(ORDERS_TYPE.LOCAL_MOVE);
+
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.RETIREMENT);
+ expect(ordersTypeDropdown).toHaveValue(ORDERS_TYPE.RETIREMENT);
+
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.SEPARATION);
+ expect(ordersTypeDropdown).toHaveValue(ORDERS_TYPE.SEPARATION);
+
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.TEMPORARY_DUTY);
+ expect(ordersTypeDropdown).toHaveValue(ORDERS_TYPE.TEMPORARY_DUTY);
+
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS);
+ expect(ordersTypeDropdown).toHaveValue(ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS);
+
+ await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.STUDENT_TRAVEL);
+ expect(ordersTypeDropdown).toHaveValue(ORDERS_TYPE.STUDENT_TRAVEL);
+ });
+
it('shows an error message if trying to submit an invalid form', async () => {
const { getByRole, findAllByRole, getByLabelText } = render(
@@ -194,3 +228,122 @@ describe('AddOrdersForm - OCONUS and Accompanied Tour Test', () => {
});
});
});
+
+describe('AddOrdersForm - Student Travel, Early Return of Dependents Test', () => {
+ it('has dependents is yes and disabled when order type is student travel', async () => {
+ isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true));
+
+ render(
+
+
+ ,
+ );
+
+ await userEvent.selectOptions(screen.getByLabelText('Orders type'), ORDERS_TYPE.STUDENT_TRAVEL);
+
+ const hasDependentsYes = screen.getByLabelText('Yes');
+ const hasDependentsNo = screen.getByLabelText('No');
+
+ await waitFor(() => {
+ expect(hasDependentsYes).toBeChecked();
+ expect(hasDependentsYes).toBeDisabled();
+ expect(hasDependentsNo).toBeDisabled();
+ });
+ });
+
+ it('has dependents is yes and disabled when order type is early return', async () => {
+ isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true));
+
+ render(
+
+
+ ,
+ );
+
+ await userEvent.selectOptions(screen.getByLabelText('Orders type'), ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS);
+ const hasDependentsYes = screen.getByLabelText('Yes');
+ const hasDependentsNo = screen.getByLabelText('No');
+
+ await waitFor(() => {
+ expect(hasDependentsYes).toBeChecked();
+ expect(hasDependentsYes).toBeDisabled();
+ expect(hasDependentsNo).toBeDisabled();
+ });
+ });
+
+ it('has dependents becomes disabled and then re-enabled for order type student travel', async () => {
+ isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true));
+
+ render(
+
+
+ ,
+ );
+
+ await userEvent.selectOptions(screen.getByLabelText('Orders type'), ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION);
+
+ const hasDependentsYesPermChg = screen.getByLabelText('Yes');
+ const hasDependentsNoPermChg = screen.getByLabelText('No');
+
+ await waitFor(() => {
+ expect(hasDependentsYesPermChg).not.toBeChecked();
+ expect(hasDependentsYesPermChg).toBeEnabled();
+ expect(hasDependentsNoPermChg).not.toBeChecked();
+ expect(hasDependentsNoPermChg).toBeEnabled();
+ });
+
+ // set order type to value that disables and defaults "has dependents"
+ await userEvent.selectOptions(screen.getByLabelText('Orders type'), ORDERS_TYPE.STUDENT_TRAVEL);
+
+ // set order type to value the re-enables "has dependents"
+ await userEvent.selectOptions(screen.getByLabelText('Orders type'), ORDERS_TYPE.LOCAL_MOVE);
+
+ const hasDependentsYesLocalMove = screen.getByLabelText('Yes');
+ const hasDependentsNoLocalMove = screen.getByLabelText('No');
+
+ await waitFor(() => {
+ expect(hasDependentsYesLocalMove).not.toBeChecked();
+ expect(hasDependentsYesLocalMove).toBeEnabled();
+ expect(hasDependentsNoLocalMove).not.toBeChecked();
+ expect(hasDependentsNoLocalMove).toBeEnabled();
+ });
+ });
+
+ it('has dependents becomes disabled and then re-enabled for order type early return', async () => {
+ isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true));
+
+ render(
+
+
+ ,
+ );
+
+ await userEvent.selectOptions(screen.getByLabelText('Orders type'), ORDERS_TYPE.PERMANENT_CHANGE_OF_STATION);
+
+ const hasDependentsYesPermChg = screen.getByLabelText('Yes');
+ const hasDependentsNoPermChg = screen.getByLabelText('No');
+
+ await waitFor(() => {
+ expect(hasDependentsYesPermChg).not.toBeChecked();
+ expect(hasDependentsYesPermChg).toBeEnabled();
+ expect(hasDependentsNoPermChg).not.toBeChecked();
+ expect(hasDependentsNoPermChg).toBeEnabled();
+ });
+
+ // set order type to value that disables and defaults "has dependents"
+ await userEvent.selectOptions(screen.getByLabelText('Orders type'), ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS);
+
+ // set order type to value the re-enables "has dependents"
+ await userEvent.selectOptions(screen.getByLabelText('Orders type'), ORDERS_TYPE.LOCAL_MOVE);
+
+ const hasDependentsYesLocalMove = screen.getByLabelText('Yes');
+ const hasDependentsNoLocalMove = screen.getByLabelText('No');
+
+ await waitFor(() => {
+ expect(hasDependentsYesLocalMove).not.toBeChecked();
+ expect(hasDependentsYesLocalMove).toBeEnabled();
+ expect(hasDependentsNoLocalMove).not.toBeChecked();
+ expect(hasDependentsNoLocalMove).toBeEnabled();
+ });
+ });
+});
From 2dd1c152386bbc5aed3562016a4d186e57c3cbc4 Mon Sep 17 00:00:00 2001
From: KonstanceH
Date: Fri, 20 Dec 2024 15:36:59 +0000
Subject: [PATCH 12/37] added test
---
.../mto_shipment/shipment_approver_test.go | 81 ++++++++++++++++++-
1 file changed, 80 insertions(+), 1 deletion(-)
diff --git a/pkg/services/mto_shipment/shipment_approver_test.go b/pkg/services/mto_shipment/shipment_approver_test.go
index 7033031584f..3a0b923c30c 100644
--- a/pkg/services/mto_shipment/shipment_approver_test.go
+++ b/pkg/services/mto_shipment/shipment_approver_test.go
@@ -255,7 +255,7 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() {
}
})
- suite.Run("Given international mtoShipment is approved successfully pre-approved mtoServiceItems are created NTS", func() {
+ suite.Run("Given international mtoShipment is approved successfully pre-approved mtoServiceItems are created NTS CONUS to OCONUS", func() {
storageFacility := factory.BuildStorageFacility(suite.DB(), []factory.Customization{
{
Model: models.StorageFacility{
@@ -333,6 +333,85 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() {
}
})
+ suite.Run("Given international mtoShipment is approved successfully pre-approved mtoServiceItems are created NTS OCONUS to CONUS", func() {
+ storageFacility := factory.BuildStorageFacility(suite.DB(), []factory.Customization{
+ {
+ Model: models.StorageFacility{
+ FacilityName: *models.StringPointer("Test Storage Name"),
+ Email: models.StringPointer("old@email.com"),
+ LotNumber: models.StringPointer("Test lot number"),
+ Phone: models.StringPointer("555-555-5555"),
+ },
+ },
+ {
+ Model: models.Address{
+ StreetAddress1: "Tester Address",
+ City: "Des Moines",
+ State: "IA",
+ PostalCode: "50314",
+ IsOconus: models.BoolPointer(false),
+ },
+ },
+ }, nil)
+
+ internationalShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{
+ {
+ Model: models.Move{
+ Status: models.MoveStatusAPPROVED,
+ },
+ },
+ {
+ Model: models.Address{
+ StreetAddress1: "JBER",
+ City: "Anchorage",
+ State: "AK",
+ PostalCode: "99507",
+ IsOconus: models.BoolPointer(true),
+ },
+ Type: &factory.Addresses.PickupAddress,
+ },
+ {
+ Model: models.MTOShipment{
+ MarketCode: models.MarketCodeInternational,
+ Status: models.MTOShipmentStatusSubmitted,
+ ShipmentType: models.MTOShipmentTypeHHGIntoNTS,
+ },
+ },
+ {
+ Model: storageFacility,
+ LinkOnly: true,
+ },
+ }, nil)
+ internationalShipmentEtag := etag.GenerateEtag(internationalShipment.UpdatedAt)
+
+ shipmentRouter := NewShipmentRouter()
+ var serviceItemCreator services.MTOServiceItemCreator
+ var planner route.Planner
+ var moveWeights services.MoveWeights
+
+ // Approve international shipment
+ shipmentApprover := NewShipmentApprover(shipmentRouter, serviceItemCreator, planner, moveWeights)
+ _, err := shipmentApprover.ApproveShipment(suite.AppContextForTest(), internationalShipment.ID, internationalShipmentEtag)
+ suite.NoError(err)
+
+ // Get created pre approved service items
+ var serviceItems []models.MTOServiceItem
+ err2 := suite.AppContextForTest().DB().EagerPreload("ReService").Where("mto_shipment_id = ?", internationalShipment.ID).Order("created_at asc").All(&serviceItems)
+ suite.NoError(err2)
+
+ expectedReserviceCodes := []models.ReServiceCode{
+ models.ReServiceCodeISLH,
+ models.ReServiceCodePODFSC,
+ models.ReServiceCodeINPK,
+ }
+
+ suite.Equal(len(expectedReserviceCodes), len(serviceItems))
+ for i := 0; i < len(serviceItems); i++ {
+ actualReServiceCode := serviceItems[i].ReService.Code
+ suite.True(slices.Contains(expectedReserviceCodes, actualReServiceCode))
+ }
+ })
+
suite.Run("If the mtoShipment is approved successfully it should create approved mtoServiceItems", func() {
subtestData := suite.createApproveShipmentSubtestData()
appCtx := subtestData.appCtx
From 08332c37dd86545576eefa45e2631af81de03558 Mon Sep 17 00:00:00 2001
From: KonstanceH
Date: Fri, 20 Dec 2024 15:43:10 +0000
Subject: [PATCH 13/37] fixed asserts
---
pkg/services/mto_shipment/shipment_approver_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pkg/services/mto_shipment/shipment_approver_test.go b/pkg/services/mto_shipment/shipment_approver_test.go
index 3a0b923c30c..f79f81677a3 100644
--- a/pkg/services/mto_shipment/shipment_approver_test.go
+++ b/pkg/services/mto_shipment/shipment_approver_test.go
@@ -323,6 +323,7 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() {
expectedReserviceCodes := []models.ReServiceCode{
models.ReServiceCodeISLH,
+ models.ReServiceCodePODFSC,
models.ReServiceCodeINPK,
}
@@ -401,7 +402,6 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() {
expectedReserviceCodes := []models.ReServiceCode{
models.ReServiceCodeISLH,
- models.ReServiceCodePODFSC,
models.ReServiceCodeINPK,
}
From 0efdd5421799b474c42501bc9277b07f3a138c1e Mon Sep 17 00:00:00 2001
From: KonstanceH
Date: Fri, 20 Dec 2024 16:08:35 +0000
Subject: [PATCH 14/37] should be it
---
pkg/services/mto_shipment/shipment_approver_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pkg/services/mto_shipment/shipment_approver_test.go b/pkg/services/mto_shipment/shipment_approver_test.go
index f79f81677a3..3a0b923c30c 100644
--- a/pkg/services/mto_shipment/shipment_approver_test.go
+++ b/pkg/services/mto_shipment/shipment_approver_test.go
@@ -323,7 +323,6 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() {
expectedReserviceCodes := []models.ReServiceCode{
models.ReServiceCodeISLH,
- models.ReServiceCodePODFSC,
models.ReServiceCodeINPK,
}
@@ -402,6 +401,7 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() {
expectedReserviceCodes := []models.ReServiceCode{
models.ReServiceCodeISLH,
+ models.ReServiceCodePODFSC,
models.ReServiceCodeINPK,
}
From 7bc8a3529f36f3cbba305c238c15e3e28129db10 Mon Sep 17 00:00:00 2001
From: JamesHawks224 <146897935+JamesHawks224@users.noreply.github.com>
Date: Fri, 3 Jan 2025 17:50:33 +0000
Subject: [PATCH 15/37] updated test cases
---
migrations/app/migrations_manifest.txt | 2 +-
.../AddOrdersForm/AddOrdersForm.test.jsx | 18 ++++++++++++++++++
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt
index 2b66fac7861..5c5d0b075f2 100644
--- a/migrations/app/migrations_manifest.txt
+++ b/migrations/app/migrations_manifest.txt
@@ -1051,9 +1051,9 @@
20241203024453_add_ppm_max_incentive_column.up.sql
20241204155919_update_ordering_proc.up.sql
20241204210208_retroactive_update_of_ppm_max_and_estimated_incentives_prd.up.sql
-20241218201833_add_PPPO_BASE_ELIZABETH.up.sql
20241217163231_update_duty_locations_bad_zips.up.sql
20241217180136_add_AK_zips_to_zip3_distances.up.sql
+20241218201833_add_PPPO_BASE_ELIZABETH.up.sql
20241220171035_add_additional_AK_zips_to_zip3_distances.up.sql
20241227153723_remove_empty_string_emplid_values.up.sql
20241230190638_remove_AK_zips_from_zip3.up.sql
diff --git a/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx b/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx
index 43d57b49e46..e2698ead501 100644
--- a/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx
+++ b/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx
@@ -295,6 +295,15 @@ describe('AddOrdersForm - Student Travel, Early Return of Dependents Test', () =
// set order type to value that disables and defaults "has dependents"
await userEvent.selectOptions(screen.getByLabelText('Orders type'), ORDERS_TYPE.STUDENT_TRAVEL);
+ const hasDependentsYesStudent = screen.getByLabelText('Yes');
+ const hasDependentsNoStudent = screen.getByLabelText('No');
+
+ await waitFor(() => {
+ expect(hasDependentsYesStudent).toBeChecked();
+ expect(hasDependentsYesStudent).toBeDisabled();
+ expect(hasDependentsNoStudent).toBeDisabled();
+ });
+
// set order type to value the re-enables "has dependents"
await userEvent.selectOptions(screen.getByLabelText('Orders type'), ORDERS_TYPE.LOCAL_MOVE);
@@ -333,6 +342,15 @@ describe('AddOrdersForm - Student Travel, Early Return of Dependents Test', () =
// set order type to value that disables and defaults "has dependents"
await userEvent.selectOptions(screen.getByLabelText('Orders type'), ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS);
+ const hasDependentsYesEarly = screen.getByLabelText('Yes');
+ const hasDependentsNoEarly = screen.getByLabelText('No');
+
+ await waitFor(() => {
+ expect(hasDependentsYesEarly).toBeChecked();
+ expect(hasDependentsYesEarly).toBeDisabled();
+ expect(hasDependentsNoEarly).toBeDisabled();
+ });
+
// set order type to value the re-enables "has dependents"
await userEvent.selectOptions(screen.getByLabelText('Orders type'), ORDERS_TYPE.LOCAL_MOVE);
From 6449f4d8fa01c7d5b850b53a347b736b597901a8 Mon Sep 17 00:00:00 2001
From: KonstanceH
Date: Tue, 7 Jan 2025 16:22:00 +0000
Subject: [PATCH 16/37] maybe fix factory for oconus to conus
---
pkg/factory/mto_shipment_factory.go | 6 +++++-
pkg/services/mto_shipment/shipment_approver_test.go | 2 +-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/pkg/factory/mto_shipment_factory.go b/pkg/factory/mto_shipment_factory.go
index 1c7858d572f..025a94fc09c 100644
--- a/pkg/factory/mto_shipment_factory.go
+++ b/pkg/factory/mto_shipment_factory.go
@@ -57,7 +57,7 @@ func buildMTOShipmentWithBuildType(db *pop.Connection, customs []Customization,
defaultStatus = models.MTOShipmentStatusDraft
buildStorageFacility = hasStorageFacilityCustom
shipmentHasPickupDetails = true
- shipmentHasDeliveryDetails = false
+ shipmentHasDeliveryDetails = true
case mtoShipmentNTSR:
defaultShipmentType = models.MTOShipmentTypeHHGOutOfNTSDom
defaultStatus = models.MTOShipmentStatusDraft
@@ -83,6 +83,10 @@ func buildMTOShipmentWithBuildType(db *pop.Connection, customs []Customization,
MarketCode: defaultMarketCode,
}
+ if newMTOShipment.ShipmentType == models.MTOShipmentTypeHHGIntoNTS && newMTOShipment.StorageFacility != nil {
+ newMTOShipment.DestinationAddress = &newMTOShipment.StorageFacility.Address
+ }
+
if cMtoShipment.Status == models.MTOShipmentStatusApproved {
approvedDate := time.Date(GHCTestYear, time.March, 20, 0, 0, 0, 0, time.UTC)
newMTOShipment.ApprovedDate = &approvedDate
diff --git a/pkg/services/mto_shipment/shipment_approver_test.go b/pkg/services/mto_shipment/shipment_approver_test.go
index 3a0b923c30c..3ecbc0a203f 100644
--- a/pkg/services/mto_shipment/shipment_approver_test.go
+++ b/pkg/services/mto_shipment/shipment_approver_test.go
@@ -354,7 +354,7 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() {
},
}, nil)
- internationalShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{
+ internationalShipment := factory.BuildNTSShipment(suite.DB(), []factory.Customization{
{
Model: models.Move{
Status: models.MoveStatusAPPROVED,
From b5e5c229128fb3a7d3ba3b4f929febfc1b08f266 Mon Sep 17 00:00:00 2001
From: KonstanceH
Date: Tue, 7 Jan 2025 16:56:58 +0000
Subject: [PATCH 17/37] update factory test
---
pkg/factory/mto_shipment_factory_test.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pkg/factory/mto_shipment_factory_test.go b/pkg/factory/mto_shipment_factory_test.go
index ea43a0a0373..60403a50f0d 100644
--- a/pkg/factory/mto_shipment_factory_test.go
+++ b/pkg/factory/mto_shipment_factory_test.go
@@ -450,9 +450,9 @@ func (suite *FactorySuite) TestBuildMTOShipment() {
suite.NotNil(ntsShipment.PrimeActualWeight)
suite.Nil(ntsShipment.StorageFacility)
suite.NotNil(ntsShipment.ScheduledPickupDate)
- suite.Nil(ntsShipment.RequestedDeliveryDate)
+ suite.NotNil(ntsShipment.RequestedDeliveryDate)
suite.Nil(ntsShipment.ActualDeliveryDate)
- suite.Nil(ntsShipment.ScheduledDeliveryDate)
+ suite.NotNil(ntsShipment.ScheduledDeliveryDate)
})
suite.Run("Successful creation of NTSShipment with storage facility", func() {
From 9210233dcfdd0a9d5f9ed429f0971f32311ddd87 Mon Sep 17 00:00:00 2001
From: Daniel Jordan
Date: Tue, 7 Jan 2025 21:11:34 +0000
Subject: [PATCH 18/37] initial commit, workin on it
---
pkg/models/re_contract.go | 32 ++++++++++
pkg/models/re_rate_area.go | 4 +-
pkg/models/re_rate_area_test.go | 6 +-
.../per_unit_cents_lookup.go | 8 +--
.../shipment_address_update_requester.go | 59 ++++++++++++-------
5 files changed, 78 insertions(+), 31 deletions(-)
diff --git a/pkg/models/re_contract.go b/pkg/models/re_contract.go
index 2c4b4a28e35..c0576ce403b 100644
--- a/pkg/models/re_contract.go
+++ b/pkg/models/re_contract.go
@@ -1,12 +1,17 @@
package models
import (
+ "database/sql"
+ "fmt"
"time"
"github.com/gobuffalo/pop/v6"
"github.com/gobuffalo/validate/v3"
"github.com/gobuffalo/validate/v3/validators"
"github.com/gofrs/uuid"
+
+ "github.com/transcom/mymove/pkg/appcontext"
+ "github.com/transcom/mymove/pkg/apperror"
)
// ReContract represents a contract with pricing information
@@ -32,3 +37,30 @@ func (r *ReContract) Validate(_ *pop.Connection) (*validate.Errors, error) {
&validators.StringIsPresent{Field: r.Name, Name: "Name"},
), nil
}
+
+func FetchContractForMove(appCtx appcontext.AppContext, moveID uuid.UUID) (ReContract, error) {
+ var move Move
+ err := appCtx.DB().Find(&move, moveID)
+ if err != nil {
+ if err == sql.ErrNoRows {
+ return ReContract{}, apperror.NewNotFoundError(moveID, "looking for Move")
+ }
+ return ReContract{}, err
+ }
+
+ if move.AvailableToPrimeAt == nil {
+ return ReContract{}, apperror.NewConflictError(moveID, "unable to pick contract because move is not available to prime")
+ }
+
+ var contractYear ReContractYear
+ err = appCtx.DB().EagerPreload("Contract").Where("? between start_date and end_date", move.AvailableToPrimeAt).
+ First(&contractYear)
+ if err != nil {
+ if err == sql.ErrNoRows {
+ return ReContract{}, apperror.NewNotFoundError(uuid.Nil, fmt.Sprintf("no contract year found for %s", move.AvailableToPrimeAt.String()))
+ }
+ return ReContract{}, err
+ }
+
+ return contractYear.Contract, nil
+}
diff --git a/pkg/models/re_rate_area.go b/pkg/models/re_rate_area.go
index 7b613b42a28..72ae7116ab7 100644
--- a/pkg/models/re_rate_area.go
+++ b/pkg/models/re_rate_area.go
@@ -56,8 +56,8 @@ func FetchReRateAreaItem(tx *pop.Connection, contractID uuid.UUID, code string)
}
// a db stored proc that takes in an address id & a service code to get the rate area id for an address
-func FetchRateAreaID(db *pop.Connection, addressID uuid.UUID, serviceID uuid.UUID, contractID uuid.UUID) (uuid.UUID, error) {
- if addressID != uuid.Nil && serviceID != uuid.Nil && contractID != uuid.Nil {
+func FetchRateAreaID(db *pop.Connection, addressID uuid.UUID, serviceID *uuid.UUID, contractID uuid.UUID) (uuid.UUID, error) {
+ if addressID != uuid.Nil && serviceID != nil && contractID != uuid.Nil {
var rateAreaID uuid.UUID
err := db.RawQuery("SELECT get_rate_area_id($1, $2, $3)", addressID, serviceID, contractID).First(&rateAreaID)
if err != nil {
diff --git a/pkg/models/re_rate_area_test.go b/pkg/models/re_rate_area_test.go
index 87f310c2088..ab279418976 100644
--- a/pkg/models/re_rate_area_test.go
+++ b/pkg/models/re_rate_area_test.go
@@ -36,16 +36,14 @@ func (suite *ModelSuite) TestFetchRateAreaID() {
service := factory.FetchReServiceByCode(suite.DB(), models.ReServiceCodeIHPK)
contract := testdatagen.FetchOrMakeReContract(suite.DB(), testdatagen.Assertions{})
address := factory.BuildAddress(suite.DB(), nil, nil)
- rateAreaId, err := models.FetchRateAreaID(suite.DB(), address.ID, service.ID, contract.ID)
+ rateAreaId, err := models.FetchRateAreaID(suite.DB(), address.ID, &service.ID, contract.ID)
suite.NotNil(rateAreaId)
suite.NoError(err)
})
suite.Run("fail - receive error when not all values are provided", func() {
- var nilUuid uuid.UUID
- contract := testdatagen.FetchOrMakeReContract(suite.DB(), testdatagen.Assertions{})
address := factory.BuildAddress(suite.DB(), nil, nil)
- rateAreaId, err := models.FetchRateAreaID(suite.DB(), address.ID, nilUuid, contract.ID)
+ rateAreaId, err := models.FetchRateAreaID(suite.DB(), address.ID, nil, uuid.Nil)
suite.Equal(uuid.Nil, rateAreaId)
suite.Error(err)
})
diff --git a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go
index b339fbf43dd..68d59f13d27 100644
--- a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go
+++ b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go
@@ -24,7 +24,7 @@ func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemP
switch p.ServiceItem.ReService.Code {
case models.ReServiceCodeIHPK:
// IHPK: Need rate area ID for the pickup address
- rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, serviceID, contractID)
+ rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, &serviceID, contractID)
if err != nil {
return "", fmt.Errorf("error fetching rate area id for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, serviceID, err)
}
@@ -43,7 +43,7 @@ func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemP
case models.ReServiceCodeIHUPK:
// IHUPK: Need rate area ID for the destination address
- rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, serviceID, contractID)
+ rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, &serviceID, contractID)
if err != nil {
return "", fmt.Errorf("error fetching rate area id for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, serviceID, err)
}
@@ -62,11 +62,11 @@ func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemP
case models.ReServiceCodeISLH:
// ISLH: Need rate area IDs for origin and destination
- originRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, serviceID, contractID)
+ originRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, &serviceID, contractID)
if err != nil {
return "", fmt.Errorf("error fetching rate area id for origin address for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, serviceID, err)
}
- destRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.DestinationAddressID, serviceID, contractID)
+ destRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.DestinationAddressID, &serviceID, contractID)
if err != nil {
return "", fmt.Errorf("error fetching rate area id for destination address for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, serviceID, err)
}
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 a0ed36fccdd..1c06fb790a8 100644
--- a/pkg/services/shipment_address_update/shipment_address_update_requester.go
+++ b/pkg/services/shipment_address_update/shipment_address_update_requester.go
@@ -52,32 +52,49 @@ func (f *shipmentAddressUpdateRequester) isAddressChangeDistanceOver50(appCtx ap
return true, nil
}
-func (f *shipmentAddressUpdateRequester) doesDeliveryAddressUpdateChangeServiceArea(appCtx appcontext.AppContext, contractID uuid.UUID, originalDeliveryAddress models.Address, newDeliveryAddress models.Address) (bool, error) {
- var existingServiceArea models.ReZip3
- var actualServiceArea models.ReZip3
+func (f *shipmentAddressUpdateRequester) doesDeliveryAddressUpdateChangeServiceOrRateArea(appCtx appcontext.AppContext, contractID uuid.UUID, originalDeliveryAddress models.Address, newDeliveryAddress models.Address, shipment models.MTOShipment) (bool, error) {
+ if shipment.MarketCode == models.MarketCodeInternational {
+ // we need to make sure we didn't change rate areas
+ originalRateArea, err := models.FetchRateAreaID(appCtx.DB(), originalDeliveryAddress.ID, nil, contractID)
+ if err != nil || originalRateArea == uuid.Nil {
+ return false, err
+ }
+ newRateArea, err := models.FetchRateAreaID(appCtx.DB(), newDeliveryAddress.ID, nil, contractID)
+ if err != nil || newRateArea == uuid.Nil {
+ return false, err
+ }
+ if originalRateArea != newRateArea {
+ return true, nil
+ } else {
+ return false, nil
+ }
+ } else {
+ var existingServiceArea models.ReZip3
+ var actualServiceArea models.ReZip3
- originalZip := originalDeliveryAddress.PostalCode[0:3]
- destinationZip := newDeliveryAddress.PostalCode[0:3]
+ originalZip := originalDeliveryAddress.PostalCode[0:3]
+ destinationZip := newDeliveryAddress.PostalCode[0:3]
- if originalZip == destinationZip {
- // If the ZIP hasn't changed, we must be in the same service area
- return false, nil
- }
+ if originalZip == destinationZip {
+ // If the ZIP hasn't changed, we must be in the same service area
+ return false, nil
+ }
- err := appCtx.DB().Where("zip3 = ?", originalZip).Where("contract_id = ?", contractID).First(&existingServiceArea)
- if err != nil {
- return false, err
- }
+ err := appCtx.DB().Where("zip3 = ?", originalZip).Where("contract_id = ?", contractID).First(&existingServiceArea)
+ if err != nil {
+ return false, err
+ }
- err = appCtx.DB().Where("zip3 = ?", destinationZip).Where("contract_id = ?", contractID).First(&actualServiceArea)
- if err != nil {
- return false, err
- }
+ err = appCtx.DB().Where("zip3 = ?", destinationZip).Where("contract_id = ?", contractID).First(&actualServiceArea)
+ if err != nil {
+ return false, err
+ }
- if existingServiceArea.DomesticServiceAreaID != actualServiceArea.DomesticServiceAreaID {
- return true, nil
+ if existingServiceArea.DomesticServiceAreaID != actualServiceArea.DomesticServiceAreaID {
+ return true, nil
+ }
+ return false, nil
}
- return false, nil
}
func (f *shipmentAddressUpdateRequester) doesDeliveryAddressUpdateChangeMileageBracket(appCtx appcontext.AppContext, originalPickupAddress models.Address, originalDeliveryAddress, newDeliveryAddress models.Address) (bool, error) {
@@ -333,7 +350,7 @@ func (f *shipmentAddressUpdateRequester) RequestShipmentDeliveryAddressUpdate(ap
return nil, err
}
- updateNeedsTOOReview, err := f.doesDeliveryAddressUpdateChangeServiceArea(appCtx, contract.ID, addressUpdate.OriginalAddress, newAddress)
+ updateNeedsTOOReview, err := f.doesDeliveryAddressUpdateChangeServiceOrRateArea(appCtx, contract.ID, addressUpdate.OriginalAddress, newAddress, shipment)
if err != nil {
return nil, err
}
From 67c56e16a53ab63f7dbda489589bdb7d5f28d670 Mon Sep 17 00:00:00 2001
From: Daniel Jordan
Date: Wed, 8 Jan 2025 17:09:26 +0000
Subject: [PATCH 19/37] added functionality, waiting on answers to questions
for 50 mile changes for international shipments
---
pkg/models/re_oconus_rate_areas.go | 14 +++++++
pkg/models/re_rate_area.go | 16 +++++++-
.../shipment_address_update_requester.go | 37 +++++++++++++------
.../AddressUpdatePreview.jsx | 29 ++++++++++-----
...hipmentAddressUpdateReviewRequestModal.jsx | 5 +--
5 files changed, 76 insertions(+), 25 deletions(-)
diff --git a/pkg/models/re_oconus_rate_areas.go b/pkg/models/re_oconus_rate_areas.go
index 72b85773159..84def705f95 100644
--- a/pkg/models/re_oconus_rate_areas.go
+++ b/pkg/models/re_oconus_rate_areas.go
@@ -3,6 +3,7 @@ package models
import (
"time"
+ "github.com/gobuffalo/pop/v6"
"github.com/gofrs/uuid"
)
@@ -19,3 +20,16 @@ type OconusRateArea struct {
func (o OconusRateArea) TableName() string {
return "re_oconus_rate_areas"
}
+
+func FetchOconusRateArea(db *pop.Connection, zip string) (*OconusRateArea, error) {
+ var reOconusRateArea OconusRateArea
+ err := db.Q().
+ InnerJoin("re_rate_areas ra", "re_oconus_rate_areas.rate_area_id = ra.id").
+ InnerJoin("us_post_region_cities upc", "upc.id = re_oconus_rate_areas.us_post_region_cities_id").
+ Where("upc.uspr_zip_id = ?", zip).
+ First(&reOconusRateArea)
+ if err != nil {
+ return nil, err
+ }
+ return &reOconusRateArea, nil
+}
diff --git a/pkg/models/re_rate_area.go b/pkg/models/re_rate_area.go
index 72ae7116ab7..8eb7c56328f 100644
--- a/pkg/models/re_rate_area.go
+++ b/pkg/models/re_rate_area.go
@@ -57,7 +57,7 @@ func FetchReRateAreaItem(tx *pop.Connection, contractID uuid.UUID, code string)
// a db stored proc that takes in an address id & a service code to get the rate area id for an address
func FetchRateAreaID(db *pop.Connection, addressID uuid.UUID, serviceID *uuid.UUID, contractID uuid.UUID) (uuid.UUID, error) {
- if addressID != uuid.Nil && serviceID != nil && contractID != uuid.Nil {
+ if addressID != uuid.Nil && contractID != uuid.Nil {
var rateAreaID uuid.UUID
err := db.RawQuery("SELECT get_rate_area_id($1, $2, $3)", addressID, serviceID, contractID).First(&rateAreaID)
if err != nil {
@@ -67,3 +67,17 @@ func FetchRateAreaID(db *pop.Connection, addressID uuid.UUID, serviceID *uuid.UU
}
return uuid.Nil, fmt.Errorf("error fetching rate area ID - required parameters not provided")
}
+
+func FetchConusRateAreaByPostalCode(db *pop.Connection, zip string, contractID uuid.UUID) (*ReRateArea, error) {
+ var reRateArea ReRateArea
+ postalCode := zip[0:3]
+ err := db.Q().
+ InnerJoin("re_zip3s rz", "rz.rate_area_id = re_rate_areas.id").
+ Where("zip3 = ?", postalCode).
+ Where("re_rate_areas.contract_id = ?", contractID).
+ First(&reRateArea)
+ if err != nil {
+ return nil, err
+ }
+ return &reRateArea, nil
+}
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 1c06fb790a8..a1fef897a65 100644
--- a/pkg/services/shipment_address_update/shipment_address_update_requester.go
+++ b/pkg/services/shipment_address_update/shipment_address_update_requester.go
@@ -38,10 +38,10 @@ func NewShipmentAddressUpdateRequester(planner route.Planner, addressCreator ser
}
}
-func (f *shipmentAddressUpdateRequester) isAddressChangeDistanceOver50(appCtx appcontext.AppContext, addressUpdate models.ShipmentAddressUpdate) (bool, error) {
+func (f *shipmentAddressUpdateRequester) isAddressChangeDistanceOver50(appCtx appcontext.AppContext, addressUpdate models.ShipmentAddressUpdate, isInternationalShipment bool) (bool, error) {
- //We calculate and set the distance between the old and new address
- distance, err := f.planner.ZipTransitDistance(appCtx, addressUpdate.OriginalAddress.PostalCode, addressUpdate.NewAddress.PostalCode, false, false)
+ // We calculate and set the distance between the old and new address
+ distance, err := f.planner.ZipTransitDistance(appCtx, addressUpdate.OriginalAddress.PostalCode, addressUpdate.NewAddress.PostalCode, false, isInternationalShipment)
if err != nil {
return false, err
}
@@ -53,17 +53,30 @@ func (f *shipmentAddressUpdateRequester) isAddressChangeDistanceOver50(appCtx ap
}
func (f *shipmentAddressUpdateRequester) doesDeliveryAddressUpdateChangeServiceOrRateArea(appCtx appcontext.AppContext, contractID uuid.UUID, originalDeliveryAddress models.Address, newDeliveryAddress models.Address, shipment models.MTOShipment) (bool, error) {
+ // international shipments find their rate areas differently than domestic
if shipment.MarketCode == models.MarketCodeInternational {
- // we need to make sure we didn't change rate areas
+ // we already have the origin address in the db so we can check the rate area using the db func
originalRateArea, err := models.FetchRateAreaID(appCtx.DB(), originalDeliveryAddress.ID, nil, contractID)
if err != nil || originalRateArea == uuid.Nil {
return false, err
}
- newRateArea, err := models.FetchRateAreaID(appCtx.DB(), newDeliveryAddress.ID, nil, contractID)
- if err != nil || newRateArea == uuid.Nil {
+ // since the new address isn't created yet we can't use the db func since it doesn't have an id,
+ // we need to manually find the rate area using the postal code
+ var updateRateArea uuid.UUID
+ newRateArea, err := models.FetchOconusRateArea(appCtx.DB(), newDeliveryAddress.PostalCode)
+ if err != nil && err != sql.ErrNoRows {
return false, err
+ } else if err == sql.ErrNoRows { // if we got no rows then the new address is likely CONUS
+ newRateArea, err := models.FetchConusRateAreaByPostalCode(appCtx.DB(), newDeliveryAddress.PostalCode, contractID)
+ if err != nil && err != sql.ErrNoRows {
+ return false, err
+ }
+ updateRateArea = newRateArea.ID
+ } else {
+ updateRateArea = newRateArea.RateAreaId
}
- if originalRateArea != newRateArea {
+ // if these are different, we need the TOO to approve this request since it will change ISLH pricing
+ if originalRateArea != updateRateArea {
return true, nil
} else {
return false, nil
@@ -355,7 +368,8 @@ func (f *shipmentAddressUpdateRequester) RequestShipmentDeliveryAddressUpdate(ap
return nil, err
}
- if !updateNeedsTOOReview {
+ // international shipments don't need to be concerned with shorthaul/linehaul
+ if !updateNeedsTOOReview && shipment.MarketCode != models.MarketCodeInternational {
if shipment.ShipmentType == models.MTOShipmentTypeHHG {
updateNeedsTOOReview, err = f.doesDeliveryAddressUpdateChangeShipmentPricingType(*shipment.PickupAddress, addressUpdate.OriginalAddress, newAddress)
if err != nil {
@@ -371,7 +385,7 @@ func (f *shipmentAddressUpdateRequester) RequestShipmentDeliveryAddressUpdate(ap
}
}
- if !updateNeedsTOOReview {
+ if !updateNeedsTOOReview && shipment.MarketCode != models.MarketCodeInternational {
if shipment.ShipmentType == models.MTOShipmentTypeHHG {
updateNeedsTOOReview, err = f.doesDeliveryAddressUpdateChangeMileageBracket(appCtx, *shipment.PickupAddress, addressUpdate.OriginalAddress, newAddress)
if err != nil {
@@ -388,7 +402,8 @@ func (f *shipmentAddressUpdateRequester) RequestShipmentDeliveryAddressUpdate(ap
}
if !updateNeedsTOOReview {
- updateNeedsTOOReview, err = f.isAddressChangeDistanceOver50(appCtx, addressUpdate)
+ internationalShipment := shipment.MarketCode == models.MarketCodeInternational
+ updateNeedsTOOReview, err = f.isAddressChangeDistanceOver50(appCtx, addressUpdate, internationalShipment)
if err != nil {
return nil, err
}
@@ -407,7 +422,7 @@ func (f *shipmentAddressUpdateRequester) RequestShipmentDeliveryAddressUpdate(ap
return apperror.NewQueryError("ShipmentAddressUpdate", txnErr, "error saving shipment address update request")
}
- //Get the move
+ // Get the move
var move models.Move
err := txnAppCtx.DB().Find(&move, shipment.MoveTaskOrderID)
if err != nil {
diff --git a/src/components/Office/AddressUpdatePreview/AddressUpdatePreview.jsx b/src/components/Office/AddressUpdatePreview/AddressUpdatePreview.jsx
index 74a2ac1e06b..7ab6cd9cf4b 100644
--- a/src/components/Office/AddressUpdatePreview/AddressUpdatePreview.jsx
+++ b/src/components/Office/AddressUpdatePreview/AddressUpdatePreview.jsx
@@ -9,23 +9,34 @@ import DataTable from 'components/DataTable/index';
import { formatTwoLineAddress } from 'utils/shipmentDisplay';
import DataTableWrapper from 'components/DataTableWrapper';
import { ShipmentAddressUpdateShape } from 'types';
+import { MARKET_CODES } from 'shared/constants';
-const AddressUpdatePreview = ({ deliveryAddressUpdate }) => {
+const AddressUpdatePreview = ({ deliveryAddressUpdate, shipment }) => {
const { originalAddress, newAddress, contractorRemarks } = deliveryAddressUpdate;
const newSitMileage = deliveryAddressUpdate.newSitDistanceBetween;
+ const { marketCode } = shipment;
return (
Delivery Address
-
- If approved, the requested update to the delivery address will change one or all of the following:
- Service area.
- Mileage bracket for direct delivery.
-
- ZIP3 resulting in Domestic Shorthaul (DSH) changing to Domestic Linehaul (DLH) or vice versa.
+ {marketCode === MARKET_CODES.DOMESTIC ? (
+
+ If approved, the requested update to the delivery address will change one or all of the following:
+ Service area.
+ Mileage bracket for direct delivery.
+
+ ZIP3 resulting in Domestic Shorthaul (DSH) changing to Domestic Linehaul (DLH) or vice versa.
+
+ Approvals will result in updated pricing for this shipment. Customer may be subject to excess costs.
+
+ ) : (
+
+ If approved, the requested update to the delivery address will change one or all of the following:
+ The rate area for the international shipment destination address.
+ Pricing for the international shipping & linehaul service item.
+ Approvals will result in updated pricing for this shipment. Customer may be subject to excess costs.
- Approvals will result in updated pricing for this shipment. Customer may be subject to excess costs.
-
+ )}
{newSitMileage > 50 ? (
diff --git a/src/components/Office/ShipmentAddressUpdateReviewRequestModal/ShipmentAddressUpdateReviewRequestModal.jsx b/src/components/Office/ShipmentAddressUpdateReviewRequestModal/ShipmentAddressUpdateReviewRequestModal.jsx
index 4325db1565f..cf1575395de 100644
--- a/src/components/Office/ShipmentAddressUpdateReviewRequestModal/ShipmentAddressUpdateReviewRequestModal.jsx
+++ b/src/components/Office/ShipmentAddressUpdateReviewRequestModal/ShipmentAddressUpdateReviewRequestModal.jsx
@@ -60,10 +60,7 @@ export const ShipmentAddressUpdateReviewRequestModal = ({
return (