diff --git a/.circleci/config.yml b/.circleci/config.yml index aab1b298ab4..a0df9b774a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -891,7 +891,7 @@ commands: export FEATURE_FLAG_VALIDATION_CODE_REQUIRED=false export FEATURE_FLAG_MOVE_LOCK=false export FEATURE_FLAG_OKTA_DODID_INPUT=false - export FEATURE_FLAG_HEADQUARTERS_ROLE=false + export FEATURE_FLAG_HEADQUARTERS_ROLE=true export FEATURE_FLAG_GSR_ROLE=false export FEATURE_FLAG_SAFETY_MOVE=false export FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false @@ -936,7 +936,7 @@ commands: FEATURE_FLAG_VALIDATION_CODE_REQUIRED: 'false' FEATURE_FLAG_MOVE_LOCK: 'false' FEATURE_FLAG_OKTA_DODID_INPUT: 'false' - FEATURE_FLAG_HEADQUARTERS_ROLE: 'false' + FEATURE_FLAG_HEADQUARTERS_ROLE: 'true' FEATURE_FLAG_GSR_ROLE: 'false' FEATURE_FLAG_SAFETY_MOVE: 'false' FEATURE_FLAG_MANAGE_SUPPORTING_DOCS: 'false' diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5909d3e55c9..d81901f0987 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,7 +39,6 @@ variables: CLIENT_IGNORE_BRANCH: &client_ignore_branch placeholder_branch_name SERVER_IGNORE_BRANCH: &server_ignore_branch placeholder_branch_name - #RUNNER_TAG: &runner_tag milmove RUNNER_TAG: &runner_tag milmove DOCKER_RUNNER_TAG: &docker_runner_tag eks_cluster_runner @@ -75,31 +74,31 @@ stages: - | if [[ "$DP3_ENV" == "exp" || "$DP3_ENV" == "loadtest" || "$DP3_ENV" == "demo" ]]; then export ENV=$(echo ${DP3_ENV} | tr '[:lower:]' '[:upper:]'); - export TLS_CERT=$(eval echo \$${ENV^^}_DP3_CERT); - export TLS_KEY=$(eval echo \$${ENV^^}_DP3_KEY); - export TLS_CA=$(eval echo \$${ENV^^}_DP3_CA); + export TLS_CERT=$(eval echo \$${ENV}_DP3_CERT); + export TLS_KEY=$(eval echo \$${ENV}_DP3_KEY); + export TLS_CA=$(eval echo \$${ENV}_DP3_CA); fi .setup_aws_vars_dp3: &setup_aws_vars_dp3 - | if [[ "$DP3_ENV" == "exp" || "$DP3_ENV" == "loadtest" || "$DP3_ENV" == "demo" ]]; then export ENV=$(echo ${DP3_ENV} | tr '[:lower:]' '[:upper:]'); - export AWS_DEFAULT_REGION=$(eval echo \$${ENV^^}_REGION); - export AWS_ACCOUNT_ID=$(eval echo \$${ENV^^}_ACCOUNT_ID); - export AWS_ACCESS_KEY_ID=$(eval echo \$${ENV^^}_ACCESS_KEY_ID); - export AWS_SECRET_ACCESS_KEY=$(eval echo \$${ENV^^}_SECRET_ACCESS_KEY); + export AWS_DEFAULT_REGION=$(eval echo \$${ENV}_REGION); + export AWS_ACCOUNT_ID=$(eval echo \$${ENV}_ACCOUNT_ID); + export AWS_ACCESS_KEY_ID=$(eval echo \$${ENV}_ACCESS_KEY_ID); + export AWS_SECRET_ACCESS_KEY=$(eval echo \$${ENV}_SECRET_ACCESS_KEY); fi .setup_release_dp3: &setup_release_dp3 - | if [[ "$DP3_ENV" == "exp" || "$DP3_ENV" == "loadtest" || "$DP3_ENV" == "demo" ]]; then export ENV=$(echo ${DP3_ENV} | tr '[:lower:]' '[:upper:]'); - export AWS_REGION=$(eval echo \$${ENV}_REGION); + export AWS_DEFAULT_REGION=$(eval echo \$${ENV}_REGION); export AWS_ACCOUNT_ID=$(eval echo \$${ENV}_ACCOUNT_ID); - export ECR_REPOSITORY_URI=$(echo ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com) + export ECR_REPOSITORY_URI=$(echo ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com) export APP_DOCKER_FILE=Dockerfile.dp3 export TASK_DOCKER_FILE=Dockerfile.tasks_dp3 - export APP_ENVIRONMENT=$ENV + export APP_ENVIRONMENT=$DP3_ENV fi .setup_aws_vars_stg: &setup_aws_vars_stg @@ -185,6 +184,7 @@ stages: - yarn.lock paths: - .cache/yarn + policy: pull-push .go_cache: &go_cache key: @@ -193,6 +193,7 @@ stages: paths: - $GOPATH/pkg/mod - /builds/milmove/mymove/bin + policy: pull-push .setup_generic_app_env_variables: &setup_generic_app_env_variables - | @@ -240,6 +241,58 @@ stages: export OKTA_OFFICE_GROUP_ID=notrealgroupId export OKTA_CUSTOMER_GROUP_ID=notrealcustomergroupId +# .setup_host_intergration_tests: &setup_host_intergration_tests +# - echo "Setting up /etc/hosts for local domain simulation" +# - echo "127.0.0.1 milmovelocal" | sudo tee -a /etc/hosts +# - echo "127.0.0.1 officelocal" | sudo tee -a /etc/hosts +# - echo "127.0.0.1 adminlocal" | sudo tee -a /etc/hosts +# - echo "127.0.0.1 primelocal" | sudo tee -a /etc/hosts + +.setup_env_intergration_mtls: &setup_env_intergration_mtls + - | + echo "Setting up environment variables" + export MIL_MOVE_DOD_CA_CERT=$(cat config/tls/devlocal-ca.pem) + export MIL_MOVE_DOD_TLS_CERT=$(cat config/tls/devlocal-https.pem) + export MIL_MOVE_DOD_TLS_KEY=$(cat config/tls/devlocal-https.key) + export CLIENT_AUTH_SECRET_KEY=$(cat config/tls/devlocal-client_auth_secret.key) + export LOGIN_GOV_SECRET_KEY=$(echo $E2E_LOGIN_GOV_SECRET_KEY | base64 --decode) + export HERE_MAPS_APP_ID=$E2E_HERE_MAPS_APP_ID + export HERE_MAPS_APP_CODE=$E2E_HERE_MAPS_APP_CODE + echo "Overriding application-specific configurations" + sed 's,^,export ,' config/env/review.app.env > server_env + source server_env + export HERE_MAPS_GEOCODE_ENDPOINT=https://geocoder.api.here.com/6.2/geocode.json + export HERE_MAPS_ROUTING_ENDPOINT=https://route.api.here.com/routing/7.2/calculateroute.json + export LOGIN_GOV_CALLBACK_PORT=4000 + export LOGIN_GOV_CALLBACK_PROTOCOL=http + make db_dev_create + bin/milmove migrate + mkdir -p build + touch build/index.html + bin/milmove serve 2>&1 | tee server.log & + +.e2e_tests_playwright: &e2e_tests_playwright + - | + echo "Preparing the environment" + export MIL_MOVE_DOD_CA_CERT=$(cat config/tls/devlocal-ca.pem) + export MIL_MOVE_DOD_TLS_CERT=$(cat config/tls/devlocal-https.pem) + export MIL_MOVE_DOD_TLS_KEY=$(cat config/tls/devlocal-https.key) + export CLIENT_AUTH_SECRET_KEY=$(cat config/tls/devlocal-client_auth_secret.key) + export LOGIN_GOV_SECRET_KEY=$(echo $E2E_LOGIN_GOV_SECRET_KEY | base64 --decode) + export HERE_MAPS_APP_ID=$E2E_HERE_MAPS_APP_ID + export HERE_MAPS_APP_CODE=$E2E_HERE_MAPS_APP_CODE + sed 's,^,export ,' config/env/review.app.env > server_env + source server_env + make db_dev_create + bin/milmove migrate + bin/milmove serve & + echo "Waiting for server to start" + dockerize -wait http://milmovelocal:4000 -timeout 5m + echo "Installing Playwright dependencies" + yarn install --frozen-lockfile --cache-folder ~/.cache/yarn + ./node_modules/.bin/playwright install + + sast: stage: pre_checks tags: @@ -423,6 +476,7 @@ build_storybook: - anti_virus cache: - <<: *yarn_cache + policy: pull before_script: - *setup_milmove_env - *install_yarn @@ -464,6 +518,7 @@ compile_app_client: image: $DOCKER_APP_IMAGE cache: - <<: *yarn_cache + policy: pull variables: KUBERNETES_CPU_REQUEST: "6" KUBERNETES_MEMORY_REQUEST: "8Gi" @@ -495,7 +550,9 @@ compile_app_server: image: $DOCKER_APP_IMAGE cache: - <<: *go_cache + policy: pull - <<: *yarn_cache + policy: pull variables: KUBERNETES_CPU_REQUEST: "6" KUBERNETES_MEMORY_REQUEST: "6Gi" @@ -541,7 +598,9 @@ pre_test: image: $DOCKER_APP_IMAGE cache: - <<: *go_cache + policy: pull - <<: *yarn_cache + policy: pull needs: - pre_deps_golang - pre_deps_yarn @@ -584,6 +643,7 @@ pre_test: - echo "Run spectral linter on all files" - ./scripts/ensure-spectral-lint /tmp/spectral_baseline spectral - ./scripts/pre-commit-go-mod || exit 0 + allow_failure: true after_script: - *announce_failure rules: @@ -646,6 +706,7 @@ server_test: # - go install gotest.tools/gotestsum@latest # - go mod tidy #- bin/gotestsum --junitfile server_test_report.xml --format server_test + allow_failure: true artifacts: paths: - /builds/milmove/mymove/bin/gotestsum @@ -675,6 +736,7 @@ server_test_coverage: ./scripts/ensure-go-test-coverage \ tmp/baseline-go-coverage/go-coverage.txt \ tmp/test-results/gotest/app/go-coverage.txt + allow_failure: true after_script: - *announce_failure rules: @@ -700,6 +762,7 @@ client_test: - pre_deps_yarn cache: - <<: *yarn_cache + policy: pull before_script: - *setup_milmove_env - *install_yarn @@ -788,6 +851,7 @@ integration_test_devseed: export MOVE_MIL_DOD_TLS_CERT=$(cat config/tls/devlocal-https.pem) export MOVE_MIL_DOD_TLS_KEY=$(cat config/tls/devlocal-https.key) - make db_dev_fresh + allow_failure: true after_script: - *announce_failure rules: @@ -811,6 +875,7 @@ integration_tests: script: - echo "TODO Add steps" - echo "integration_tests" + allow_failure: true after_script: - *announce_failure rules: @@ -819,15 +884,50 @@ integration_tests: integration_test_mtls: stage: test tags: - - $RUNNER_TAG + - $DOCKER_RUNNER_TAG image: $DOCKER_APP_IMAGE + services: + - name: docker:dind + alias: docker + - name: $postgres + - name: $redis + variables: + DOCKER_HOST: "tcp://docker-backend.gitlab-runner.svc.cluster.local:2375" + DOCKER_TLS_CERTDIR: "" + APPLICATION: app + DB_PASSWORD: mysecretpassword + DB_USER_LOW_PRIV: crud + DB_PASSWORD_LOW_PRIV: mysecretpassword + DB_USER: postgres + DB_HOST: localhost + DB_PORT: 5432 + DB_NAME: dev_db + DB_NAME_DEV: dev_db + MIGRATION_MANIFEST: '/builds/milmove/mymove/migrations/app/migrations_manifest.txt' + MIGRATION_PATH: 'file:///builds/milmove/mymove/migrations/app/schema;file:///builds/milmove/mymove/migrations/app/secure' + EIA_KEY: db2522a43820268a41a802a16ae9fd26 # dummy key generated with openssl rand -hex 16 + ENVIRONMENT: development + DOD_CA_PACKAGE: /builds/milmove/mymove/config/tls/milmove-cert-bundle.p7b + POSTGRES_PASSWORD: mysecretpassword + POSTGRES_DB: test_db needs: - pre_deps_yarn - compile_app_server - before_script: *setup_milmove_env + before_script: + - *setup_milmove_env + - *setup_env_intergration_mtls script: - echo "TODO Add steps" - echo "integration_test_mtls" + - echo "Waiting for server to start" + - dockerize -wait http://milmovelocal:4000 -timeout 5m + - echo "Running E2E mTLS tests" + - ./scripts/run-e2e-mtls-test + artifacts: + paths: + - test-results/ + when: always + allow_failure: true after_script: - *announce_failure rules: @@ -836,17 +936,55 @@ integration_test_mtls: integration_test_admin: stage: test tags: - - $RUNNER_TAG + - $DOCKER_RUNNER_TAG image: $DOCKER_APP_IMAGE + services: + - name: docker:dind + alias: docker + - name: $postgres + - name: $redis + variables: + DOCKER_HOST: "tcp://docker-backend.gitlab-runner.svc.cluster.local:2375" + DOCKER_TLS_CERTDIR: "" + APPLICATION: app + DB_PASSWORD: mysecretpassword + DB_USER_LOW_PRIV: crud + DB_PASSWORD_LOW_PRIV: mysecretpassword + DB_USER: postgres + DB_HOST: localhost + DB_PORT: 5432 + DB_NAME: dev_db + DB_NAME_DEV: dev_db + MIGRATION_MANIFEST: '/builds/milmove/mymove/migrations/app/migrations_manifest.txt' + MIGRATION_PATH: 'file:///builds/milmove/mymove/migrations/app/schema;file:///builds/milmove/mymove/migrations/app/secure' + EIA_KEY: db2522a43820268a41a802a16ae9fd26 # dummy key generated with openssl rand -hex 16 + ENVIRONMENT: development + DOD_CA_PACKAGE: /builds/milmove/mymove/config/tls/milmove-cert-bundle.p7b + POSTGRES_PASSWORD: mysecretpassword + POSTGRES_DB: test_db needs: - pre_deps_yarn - pre_deps_golang - compile_app_client - compile_app_server - before_script: *setup_milmove_env + before_script: + - *setup_milmove_env + - *e2e_tests_playwright script: - echo "TODO Add steps" - echo "integration_test_admin" + - echo "Running integration tests for Admin" + - ./node_modules/.bin/playwright test playwright/tests/admin \ + --reporter=html,junit \ + --trace=on \ + --workers=1 + artifacts: + paths: + - playwright-report/ + - complete-playwright-report.zip + - playwright-results.xml + when: always + allow_failure: true after_script: - *announce_failure rules: @@ -855,17 +993,56 @@ integration_test_admin: integration_test_my: stage: test tags: - - $RUNNER_TAG + - $DOCKER_RUNNER_TAG image: $DOCKER_APP_IMAGE + services: + - name: docker:dind + alias: docker + - name: $postgres + - name: $redis + variables: + DOCKER_HOST: "tcp://docker-backend.gitlab-runner.svc.cluster.local:2375" + DOCKER_TLS_CERTDIR: "" + APPLICATION: app + DB_PASSWORD: mysecretpassword + DB_USER_LOW_PRIV: crud + DB_PASSWORD_LOW_PRIV: mysecretpassword + DB_USER: postgres + DB_HOST: localhost + DB_PORT: 5432 + DB_NAME: dev_db + DB_NAME_DEV: dev_db + MIGRATION_MANIFEST: '/builds/milmove/mymove/migrations/app/migrations_manifest.txt' + MIGRATION_PATH: 'file:///builds/milmove/mymove/migrations/app/schema;file:///builds/milmove/mymove/migrations/app/secure' + EIA_KEY: db2522a43820268a41a802a16ae9fd26 # dummy key generated with openssl rand -hex 16 + ENVIRONMENT: development + DOD_CA_PACKAGE: /builds/milmove/mymove/config/tls/milmove-cert-bundle.p7b + POSTGRES_PASSWORD: mysecretpassword + POSTGRES_DB: test_db needs: - pre_deps_yarn - pre_deps_golang - compile_app_client - compile_app_server - before_script: *setup_milmove_env + before_script: + - *setup_milmove_env + - *e2e_tests_playwright script: - echo "TODO Add steps" - echo "integration_test_my" + - echo "Running integration tests for My" + - ./node_modules/.bin/playwright test playwright/tests/my \ + --reporter=html,junit \ + --trace=on \ + --workers=1 \ + --shard="$CI_NODE_INDEX/$CI_NODE_TOTAL" + artifacts: + paths: + - playwright-report/ + - complete-playwright-report.zip + - playwright-results.xml + when: always + allow_failure: true after_script: - *announce_failure rules: @@ -874,17 +1051,54 @@ integration_test_my: integration_test_office: stage: test tags: - - $RUNNER_TAG + - $DOCKER_RUNNER_TAG image: $DOCKER_APP_IMAGE + services: + - name: docker:dind + alias: docker + - name: $postgres + - name: $redis + variables: + DOCKER_HOST: "tcp://docker-backend.gitlab-runner.svc.cluster.local:2375" + DOCKER_TLS_CERTDIR: "" + APPLICATION: app + DB_PASSWORD: mysecretpassword + DB_USER_LOW_PRIV: crud + DB_PASSWORD_LOW_PRIV: mysecretpassword + DB_USER: postgres + DB_HOST: localhost + DB_PORT: 5432 + DB_NAME: dev_db + DB_NAME_DEV: dev_db + MIGRATION_MANIFEST: '/builds/milmove/mymove/migrations/app/migrations_manifest.txt' + MIGRATION_PATH: 'file:///builds/milmove/mymove/migrations/app/schema;file:///builds/milmove/mymove/migrations/app/secure' + EIA_KEY: db2522a43820268a41a802a16ae9fd26 # dummy key generated with openssl rand -hex 16 + ENVIRONMENT: development + DOD_CA_PACKAGE: /builds/milmove/mymove/config/tls/milmove-cert-bundle.p7b + POSTGRES_PASSWORD: mysecretpassword + POSTGRES_DB: test_db needs: - pre_deps_yarn - pre_deps_golang - compile_app_client - compile_app_server - before_script: *setup_milmove_env + before_script: + - *setup_milmove_env + - *e2e_tests_playwright script: - echo "TODO Add steps" - echo "integration_test_office" + - ./node_modules/.bin/playwright test playwright/tests/office \ + --reporter=html,junit \ + --trace=on \ + --workers=1 + artifacts: + paths: + - playwright-report/ + - complete-playwright-report.zip + - playwright-results.xml + when: always + allow_failure: true after_script: - *announce_failure rules: @@ -898,7 +1112,7 @@ build_push_app_dp3: stage: push tags: - $RUNNER_TAG - environment: DP3_ENV + environment: $DP3_ENV image: name: gcr.io/kaniko-project/executor:v1.14.0-debug entrypoint: [""] @@ -921,7 +1135,7 @@ build_push_migrations_dp3: stage: push tags: - $RUNNER_TAG - environment: DP3_ENV + environment: $DP3_ENV image: name: gcr.io/kaniko-project/executor:v1.14.0-debug entrypoint: [""] @@ -944,7 +1158,7 @@ build_push_tasks_dp3: stage: push tags: - $RUNNER_TAG - environment: DP3_ENV + environment: $DP3_ENV image: name: gcr.io/kaniko-project/executor:v1.14.0-debug entrypoint: [""] @@ -967,7 +1181,10 @@ push_otel_collector_image_dp3: stage: push tags: - $RUNNER_TAG - environment: DP3_ENV + environment: $DP3_ENV + before_script: + - *setup_aws_vars_dp3 + - *setup_release_dp3 image: name: $DOCKER_BASE_IMAGE entrypoint: [""] @@ -996,11 +1213,12 @@ deploy_migrations_dp3: stage: deploy tags: - $RUNNER_TAG - environment: DP3_ENV + environment: $DP3_ENV image: name: $DOCKER_APP_IMAGE entrypoint: [""] needs: + - push_otel_collector_image_dp3 - build_push_migrations_dp3 - compile_app_server - compile_app_client @@ -1026,14 +1244,17 @@ deploy_tasks_dp3: stage: deploy tags: - $RUNNER_TAG + environment: $DP3_ENV image: name: $DOCKER_APP_IMAGE entrypoint: [""] needs: + - deploy_migrations_dp3 - build_push_tasks_dp3 - compile_app_server - compile_app_client before_script: + - *setup_aws_vars_dp3 - *setup_release_dp3 script: - echo "Getting Digest from AWS" @@ -1051,7 +1272,7 @@ deploy_app_client_tls_dp3: stage: deploy tags: - $RUNNER_TAG - environment: DP3_ENV + environment: $DP3_ENV image: name: $DOCKER_APP_IMAGE entrypoint: [""] @@ -1065,6 +1286,7 @@ deploy_app_client_tls_dp3: HEALTH_CHECK: "true" before_script: - *setup_aws_vars_dp3 + - *setup_tls_vars_dp3 - *setup_release_dp3 script: # - echo "Comparing against deployed commit" @@ -1077,11 +1299,11 @@ deploy_app_client_tls_dp3: - echo "Deploying app-client-tls service" - ./scripts/ecs-deploy-service-container app-client-tls "${ECR_REPOSITORY_URI}/app@${ECR_DIGEST}" "${APP_ENVIRONMENT}" "/bin/milmove serve" - echo "Running Health Check" - # - bin/health-checker --schemes https --hosts api.demo.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --tries 10 --backoff 3 --log-level info --timeout 5m - # - echo "Running TLS Check" - # - bin/tls-checker --schemes https --hosts api.demo.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --log-level info --timeout 15m - # - echo "Checking deployed commits" - # - ./scripts/check-deployed-commit "api.demo.dp3.us" "$CI_COMMIT_SHA" ${TLS_KEY} ${TLS_CERT} ${TLS_CA} + - bin/health-checker --schemes https --hosts api.$APP_ENVIRONMENT.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --tries 10 --backoff 3 --log-level info --timeout 5m + - echo "Running TLS Check" + - bin/tls-checker --schemes https --hosts api.$APP_ENVIRONMENT.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --log-level info --timeout 15m + - echo "Checking deployed commits" + - ./scripts/check-deployed-commit "api.$APP_ENVIRONMENT.dp3.us" "$CI_COMMIT_SHA" ${TLS_KEY} ${TLS_CERT} ${TLS_CA} after_script: - *announce_failure rules: @@ -1091,7 +1313,7 @@ deploy_app_dp3: stage: deploy tags: - $RUNNER_TAG - environment: DP3_ENV + environment: $DP3_ENV image: name: $DOCKER_APP_IMAGE entrypoint: [""] @@ -1104,6 +1326,7 @@ deploy_app_dp3: OPEN_TELEMETRY_SIDECAR: "true" HEALTH_CHECK: "true" before_script: + - *setup_tls_vars_dp3 - *setup_aws_vars_dp3 - *setup_release_dp3 script: @@ -1126,11 +1349,11 @@ deploy_app_dp3: - echo "Deploying app service" - ./scripts/ecs-deploy-service-container app "${ECR_REPOSITORY_URI}/app@${ECR_DIGEST}" "${APP_ENVIRONMENT}" "/bin/milmove serve" - echo "Running Health Check" - # - bin/health-checker --schemes https --hosts my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us --tries 10 --backoff 3 --log-level info --timeout 5m - # - echo "Running TLS Check" - # - bin/tls-checker --schemes https --hosts my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us --log-level info --timeout 15m - # - echo "Checking deployed commits" - - ./scripts/check-deployed-commit "my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us" "$CI_COMMIT_SHA" + - bin/health-checker --schemes https --hosts my.$DP3_ENV.dp3.us,office.$DP3_ENV.dp3.us,admin.$DP3_ENV.dp3.us --tries 10 --backoff 3 --log-level info --timeout 5m + - echo "Running TLS Check" + - bin/tls-checker --schemes https --hosts my.$DP3_ENV.dp3.us,office.$DP3_ENV.dp3.us,admin.$DP3_ENV.dp3.us --log-level info --timeout 15m + - echo "Checking deployed commits" + - ./scripts/check-deployed-commit "my.$DP3_ENV.dp3.us,office.$DP3_ENV.dp3.us,admin.$DP3_ENV.dp3.us" "$CI_COMMIT_SHA" after_script: - *announce_failure rules: @@ -1250,6 +1473,7 @@ deploy_migrations_stg: name: $DOCKER_APP_IMAGE entrypoint: [""] needs: + - push_otel_collector_image_stg - build_push_migrations_stg - compile_app_server - compile_app_client @@ -1280,6 +1504,7 @@ deploy_tasks_stg: name: $DOCKER_APP_IMAGE entrypoint: [""] needs: + - deploy_migrations_stg - build_push_tasks_stg - compile_app_server - compile_app_client @@ -1313,6 +1538,7 @@ deploy_app_client_tls_stg: OPEN_TELEMETRY_SIDECAR: "true" HEALTH_CHECK: "true" before_script: + - *setup_tls_vars_stg - *setup_aws_vars_stg - *setup_release_stg script: @@ -1325,12 +1551,13 @@ deploy_app_client_tls_stg: - export OTEL_COLLECTOR_IMAGE="${ECR_REPOSITORY_URI}/otel-collector@${OTEL_ECR_DIGEST}" - echo "Deploying app-client-tls service" - ./scripts/ecs-deploy-service-container app-client-tls "${ECR_REPOSITORY_URI}/app@${ECR_DIGEST}" "${APP_ENVIRONMENT}" "/bin/milmove serve" + #TODO: fix domain make dynamic and pass in preferred - echo "Running Health Check" - # - bin/health-checker --schemes https --hosts api.demo.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --tries 10 --backoff 3 --log-level info --timeout 5m - # - echo "Running TLS Check" - # - bin/tls-checker --schemes https --hosts api.demo.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --log-level info --timeout 15m - # - echo "Checking deployed commits" - # - ./scripts/check-deployed-commit "api.demo.dp3.us" "$CI_COMMIT_SHA" ${TLS_KEY} ${TLS_CERT} ${TLS_CA} + - bin/health-checker --schemes https --hosts api.$APP_ENVIRONMENT.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --tries 10 --backoff 3 --log-level info --timeout 5m + - echo "Running TLS Check" + - bin/tls-checker --schemes https --hosts api.$APP_ENVIRONMENT.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --log-level info --timeout 15m + - echo "Checking deployed commits" + - ./scripts/check-deployed-commit "api.$APP_ENVIRONMENT.dp3.us" "$CI_COMMIT_SHA" ${TLS_KEY} ${TLS_CERT} ${TLS_CA} after_script: - *announce_failure rules: @@ -1353,6 +1580,7 @@ deploy_app_stg: OPEN_TELEMETRY_SIDECAR: "true" HEALTH_CHECK: "true" before_script: + - *setup_tls_vars_stg - *setup_aws_vars_stg - *setup_release_stg script: @@ -1374,12 +1602,13 @@ deploy_app_stg: - export OTEL_COLLECTOR_IMAGE="${ECR_REPOSITORY_URI}/otel-collector@${OTEL_ECR_DIGEST}" - echo "Deploying app service" - ./scripts/ecs-deploy-service-container app "${ECR_REPOSITORY_URI}/app@${ECR_DIGEST}" "${APP_ENVIRONMENT}" "/bin/milmove serve" + #TODO: fix domain make dynamic and pass in preferred - echo "Running Health Check" - # - bin/health-checker --schemes https --hosts my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us --tries 10 --backoff 3 --log-level info --timeout 5m - # - echo "Running TLS Check" - # - bin/tls-checker --schemes https --hosts my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us --log-level info --timeout 15m - # - echo "Checking deployed commits" - - ./scripts/check-deployed-commit "my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us" "$CI_COMMIT_SHA" + - bin/health-checker --schemes https --hosts my.$APP_ENVIRONMENT.dp3.us,office.$APP_ENVIRONMENT.dp3.us,admin.$APP_ENVIRONMENT.dp3.us --tries 10 --backoff 3 --log-level info --timeout 5m + - echo "Running TLS Check" + - bin/tls-checker --schemes https --hosts my.$APP_ENVIRONMENT.dp3.us,office.$APP_ENVIRONMENT.dp3.us,admin.$APP_ENVIRONMENT.dp3.us --log-level info --timeout 15m + - echo "Checking deployed commits" + - ./scripts/check-deployed-commit "my.$APP_ENVIRONMENT.dp3.us,office.$APP_ENVIRONMENT.dp3.us,admin.$APP_ENVIRONMENT.dp3.us" "$CI_COMMIT_SHA" after_script: - *announce_failure rules: @@ -1517,6 +1746,7 @@ deploy_migrations_prd: name: $DOCKER_APP_IMAGE entrypoint: [""] needs: + - push_otel_collector_image_prd - build_push_migrations_prd - compile_app_server - compile_app_client @@ -1547,6 +1777,7 @@ deploy_tasks_prd: name: $DOCKER_APP_IMAGE entrypoint: [""] needs: + - deploy_migrations_prd - build_push_tasks_prd - compile_app_server - compile_app_client @@ -1582,6 +1813,7 @@ deploy_app_client_tls_prd: OPEN_TELEMETRY_SIDECAR: "true" HEALTH_CHECK: "true" before_script: + - *setup_tls_vars_prd - *setup_aws_vars_prd - *setup_release_prd script: @@ -1594,12 +1826,13 @@ deploy_app_client_tls_prd: - export OTEL_COLLECTOR_IMAGE="${ECR_REPOSITORY_URI}/otel-collector@${OTEL_ECR_DIGEST}" - echo "Deploying app-client-tls service" - ./scripts/ecs-deploy-service-container app-client-tls "${ECR_REPOSITORY_URI}/app@${ECR_DIGEST}" "${APP_ENVIRONMENT}" "/bin/milmove serve" + #TODO: fix domain make dynamic and pass in preferred - echo "Running Health Check" - # - bin/health-checker --schemes https --hosts api.demo.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --tries 10 --backoff 3 --log-level info --timeout 5m - # - echo "Running TLS Check" - # - bin/tls-checker --schemes https --hosts api.demo.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --log-level info --timeout 15m - # - echo "Checking deployed commits" - # - ./scripts/check-deployed-commit "api.demo.dp3.us" "$CI_COMMIT_SHA" ${TLS_KEY} ${TLS_CERT} ${TLS_CA} + - bin/health-checker --schemes https --hosts api.$APP_ENVIRONMENT.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --tries 10 --backoff 3 --log-level info --timeout 5m + - echo "Running TLS Check" + - bin/tls-checker --schemes https --hosts api.$APP_ENVIRONMENT.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --log-level info --timeout 15m + - echo "Checking deployed commits" + - ./scripts/check-deployed-commit "api.$APP_ENVIRONMENT.dp3.us" "$CI_COMMIT_SHA" ${TLS_KEY} ${TLS_CERT} ${TLS_CA} after_script: - *announce_failure rules: @@ -1622,6 +1855,7 @@ deploy_app_prd: OPEN_TELEMETRY_SIDECAR: "true" HEALTH_CHECK: "true" before_script: + - *setup_tls_vars_prd - *setup_aws_vars_prd - *setup_release_prd script: @@ -1643,12 +1877,13 @@ deploy_app_prd: - export OTEL_COLLECTOR_IMAGE="${ECR_REPOSITORY_URI}/otel-collector@${OTEL_ECR_DIGEST}" - echo "Deploying app service" - ./scripts/ecs-deploy-service-container app "${ECR_REPOSITORY_URI}/app@${ECR_DIGEST}" "${APP_ENVIRONMENT}" "/bin/milmove serve" + #TODO: fix domain make dynamic and pass in preferred - echo "Running Health Check" - # - bin/health-checker --schemes https --hosts my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us --tries 10 --backoff 3 --log-level info --timeout 5m - # - echo "Running TLS Check" - # - bin/tls-checker --schemes https --hosts my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us --log-level info --timeout 15m - # - echo "Checking deployed commits" - - ./scripts/check-deployed-commit "my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us" "$CI_COMMIT_SHA" + - bin/health-checker --schemes https --hosts my.$APP_ENVIRONMENT.dp3.us,office.$APP_ENVIRONMENT.dp3.us,admin.$APP_ENVIRONMENT.dp3.us --tries 10 --backoff 3 --log-level info --timeout 5m + - echo "Running TLS Check" + - bin/tls-checker --schemes https --hosts my.$APP_ENVIRONMENT.dp3.us,office.$APP_ENVIRONMENT.dp3.us,admin.$APP_ENVIRONMENT.dp3.us --log-level info --timeout 15m + - echo "Checking deployed commits" + - ./scripts/check-deployed-commit "my.$APP_ENVIRONMENT.dp3.us,office.$APP_ENVIRONMENT.dp3.us,admin.$APP_ENVIRONMENT.dp3.us" "$CI_COMMIT_SHA" after_script: - *announce_failure rules: diff --git a/Dockerfile b/Dockerfile index afdf33674c9..d74fa75d42c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,9 @@ COPY swagger/* /swagger/ COPY build /build COPY public/static/react-file-viewer /public/static/react-file-viewer +# Mount mutable tmp for app packages like pdfcpu +VOLUME ["/tmp"] + ENTRYPOINT ["/bin/milmove"] CMD ["serve", "--logging-level=debug"] diff --git a/Dockerfile.dp3 b/Dockerfile.dp3 index e47a0746e27..b9b420cdeb0 100644 --- a/Dockerfile.dp3 +++ b/Dockerfile.dp3 @@ -1,3 +1,5 @@ +FROM debian:stable AS build-env + # hadolint ignore=DL3007 FROM gcr.io/distroless/base-debian11@sha256:ac69aa622ea5dcbca0803ca877d47d069f51bd4282d5c96977e0390d7d256455 @@ -22,6 +24,9 @@ COPY swagger/* /swagger/ COPY build /build COPY public/static/react-file-viewer /public/static/react-file-viewer +# Mount mutable tmp for app packages like pdfcpu +VOLUME ["/tmp"] + ENTRYPOINT ["/bin/milmove"] CMD ["serve", "--logging-level=debug"] diff --git a/config/tls/milmove-cert-bundle.p7b b/config/tls/milmove-cert-bundle.p7b index 85eb6a72d7f..755ddc7b08a 100644 Binary files a/config/tls/milmove-cert-bundle.p7b and b/config/tls/milmove-cert-bundle.p7b differ diff --git a/go.mod b/go.mod index 264e97343fa..f9a71dcd5fd 100644 --- a/go.mod +++ b/go.mod @@ -62,7 +62,7 @@ require ( github.com/lib/pq v1.10.9 github.com/markbates/goth v1.79.0 github.com/namsral/flag v1.7.4-pre - github.com/pdfcpu/pdfcpu v0.6.0 + github.com/pdfcpu/pdfcpu v0.9.1 github.com/pkg/errors v0.9.1 github.com/pkg/sftp v1.13.7 github.com/pterm/pterm v0.12.79 @@ -222,7 +222,7 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/microcosm-cc/bluemonday v1.0.23 // indirect @@ -237,7 +237,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/peterbourgon/diskv/v3 v3.0.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/fastuuid v1.2.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/rs/zerolog v1.29.0 // indirect @@ -259,7 +259,7 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/image v0.18.0 // indirect + golang.org/x/image v0.21.0 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect diff --git a/go.sum b/go.sum index 8a725675df2..d3fa78e83b6 100644 --- a/go.sum +++ b/go.sum @@ -473,8 +473,8 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -511,8 +511,8 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 h1:pSCLCl6joCFRnjpeojzOpEYs4q7Vditq8fySFG5ap3Y= github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pdfcpu/pdfcpu v0.6.0 h1:z4kARP5bcWa39TTYMcN/kjBnm7MvhTWjXgeYmkdAGMI= -github.com/pdfcpu/pdfcpu v0.6.0/go.mod h1:kmpD0rk8YnZj0l3qSeGBlAB+XszHUgNv//ORH/E7EYo= +github.com/pdfcpu/pdfcpu v0.9.1 h1:q8/KlBdHjkE7ZJU4ofhKG5Rjf7M6L324CVM6BMDySao= +github.com/pdfcpu/pdfcpu v0.9.1/go.mod h1:fVfOloBzs2+W2VJCCbq60XIxc3yJHAZ0Gahv1oO0gyI= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU= @@ -547,8 +547,8 @@ github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTK github.com/rickar/cal/v2 v2.1.13 h1:FENBPXxDPyL1OWGf9ZdpWGcEiGoSjt0UZED8VOxvK0c= github.com/rickar/cal/v2 v2.1.13/go.mod h1:/fdlMcx7GjPlIBibMzOM9gMvDBsrK+mOtRXdTzUqV/A= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -730,8 +730,8 @@ golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjs golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= -golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= +golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index 53ddf99cfc3..2db6282e320 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1059,6 +1059,7 @@ 20241217163231_update_duty_locations_bad_zips.up.sql 20241217180136_add_AK_zips_to_zip3_distances.up.sql 20241218201833_add_PPPO_BASE_ELIZABETH.up.sql +20241218204620_add_international_nts_service_items.up.sql 20241220171035_add_additional_AK_zips_to_zip3_distances.up.sql 20241220213134_add_destination_gbloc_db_function.up.sql 20241224172258_add_and_update_po_box_zip.up.sql @@ -1068,6 +1069,8 @@ 20241230190638_remove_AK_zips_from_zip3.up.sql 20241230190647_add_missing_AK_zips_to_zip3_distances.up.sql 20250103180420_update_pricing_proc_to_use_local_price_variable.up.sql +20250110001339_update_nts_release_enum_name.up.sql 20250110214012_homesafeconnect_cert.up.sql 20250113201232_update_estimated_pricing_procs_add_is_peak_func.up.sql 20250114164752_add_ppm_estimated_incentive_proc.up.sql +20250116200912_disable_homesafe_stg_cert.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..207c5379080 --- /dev/null +++ b/migrations/app/schema/20241218204620_add_international_nts_service_items.up.sql @@ -0,0 +1,36 @@ +-- +-- 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 + --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); diff --git a/migrations/app/schema/20250110001339_update_nts_release_enum_name.up.sql b/migrations/app/schema/20250110001339_update_nts_release_enum_name.up.sql new file mode 100644 index 00000000000..ed08a9ed160 --- /dev/null +++ b/migrations/app/schema/20250110001339_update_nts_release_enum_name.up.sql @@ -0,0 +1,25 @@ +-- Rename the existing enum value 'HHG_OUTOF_NTS_DOMESTIC' to 'HHG_OUTOF_NTS' +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_type t + JOIN pg_enum e ON t.oid = e.enumtypid + WHERE t.typname = 'mto_shipment_type' + AND e.enumlabel = 'HHG_OUTOF_NTS_DOMESTIC' + ) THEN + ALTER TYPE mto_shipment_type + RENAME VALUE 'HHG_OUTOF_NTS_DOMESTIC' TO 'HHG_OUTOF_NTS'; + END IF; +END $$; + +-- Update column comments to include all current shipment types +COMMENT ON COLUMN mto_shipments.shipment_type IS 'The type of shipment. The list includes: +1. Personally procured move (PPM) +2. Household goods move (HHG) +3. Non-temporary storage (HHG_INTO_NTS) +4. Non-temporary storage-release (HHG_OUTOF_NTS) +5. Mobile home (MOBILE_HOME) +6. Boat haul away (BOAT_HAUL_AWAY) +7. Boat tow away (BOAT_TOW_AWAY) +8. Unaccompanied baggage (UNACCOMPANIED_BAGGAGE)'; \ No newline at end of file diff --git a/migrations/app/secure/20250116200912_disable_homesafe_stg_cert.up.sql b/migrations/app/secure/20250116200912_disable_homesafe_stg_cert.up.sql new file mode 100644 index 00000000000..f9862f58a7c --- /dev/null +++ b/migrations/app/secure/20250116200912_disable_homesafe_stg_cert.up.sql @@ -0,0 +1,4 @@ +-- Local test migration. +-- This will be run on development environments. +-- It should mirror what you intend to apply on prd/stg/exp/demo +-- DO NOT include any sensitive data. diff --git a/pkg/factory/mto_shipment_factory.go b/pkg/factory/mto_shipment_factory.go index 1c7858d572f..0dc95a166e6 100644 --- a/pkg/factory/mto_shipment_factory.go +++ b/pkg/factory/mto_shipment_factory.go @@ -46,9 +46,9 @@ func buildMTOShipmentWithBuildType(db *pop.Connection, customs []Customization, setupPickupAndDelivery := true hasStorageFacilityCustom := findValidCustomization(customs, StorageFacility) != nil buildStorageFacility := - cMtoShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom || + cMtoShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS || cMtoShipment.ShipmentType == models.MTOShipmentTypeHHGIntoNTS - shipmentHasPickupDetails := cMtoShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom && cMtoShipment.ShipmentType != models.MTOShipmentTypePPM + shipmentHasPickupDetails := cMtoShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS && cMtoShipment.ShipmentType != models.MTOShipmentTypePPM shipmentHasDeliveryDetails := cMtoShipment.ShipmentType != models.MTOShipmentTypeHHGIntoNTS && cMtoShipment.ShipmentType != models.MTOShipmentTypePPM addPrimeActualWeight := true switch buildType { @@ -57,9 +57,9 @@ func buildMTOShipmentWithBuildType(db *pop.Connection, customs []Customization, defaultStatus = models.MTOShipmentStatusDraft buildStorageFacility = hasStorageFacilityCustom shipmentHasPickupDetails = true - shipmentHasDeliveryDetails = false + shipmentHasDeliveryDetails = true case mtoShipmentNTSR: - defaultShipmentType = models.MTOShipmentTypeHHGOutOfNTSDom + defaultShipmentType = models.MTOShipmentTypeHHGOutOfNTS defaultStatus = models.MTOShipmentStatusDraft buildStorageFacility = hasStorageFacilityCustom addPrimeActualWeight = false @@ -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 @@ -225,9 +229,9 @@ func BuildBaseMTOShipment(db *pop.Connection, customs []Customization, traits [] // BuildMTOShipment creates a single MTOShipment and associated set relationships // It will make a move record, if one is not provided. -// It will make pickup addresses if the shipment type is not one of (HHGOutOfNTSDom, PPM) +// It will make pickup addresses if the shipment type is not one of (HHGOutOfNTS, PPM) // It will make delivery addresses if the shipment type is not one of (HHGIntoNTSDom, PPM) -// It will make a storage facility if the shipment type is HHGOutOfNTSDom +// It will make a storage facility if the shipment type is HHGOutOfNTS func BuildMTOShipment(db *pop.Connection, customs []Customization, traits []Trait) models.MTOShipment { return buildMTOShipmentWithBuildType(db, customs, traits, mtoShipmentBuild) } diff --git a/pkg/factory/mto_shipment_factory_test.go b/pkg/factory/mto_shipment_factory_test.go index ea43a0a0373..db0c04dd979 100644 --- a/pkg/factory/mto_shipment_factory_test.go +++ b/pkg/factory/mto_shipment_factory_test.go @@ -298,7 +298,7 @@ func (suite *FactorySuite) TestBuildMTOShipment() { ID: uuid.FromStringOrNil("acf7b357-5cad-40e2-baa7-dedc1d4cf04c"), PrimeEstimatedWeight: &estimatedWeight, PrimeActualWeight: &actualWeight, - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, ApprovedDate: models.TimePointer(time.Now()), Status: models.MTOShipmentStatusApproved, } @@ -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() { @@ -470,7 +470,7 @@ func (suite *FactorySuite) TestBuildMTOShipment() { suite.Run("Successful creation of NTSRShipment", func() { ntsrShipment := BuildNTSRShipment(suite.DB(), nil, nil) - suite.Equal(models.MTOShipmentTypeHHGOutOfNTSDom, ntsrShipment.ShipmentType) + suite.Equal(models.MTOShipmentTypeHHGOutOfNTS, ntsrShipment.ShipmentType) suite.False(ntsrShipment.MoveTaskOrderID.IsNil()) suite.False(ntsrShipment.MoveTaskOrder.ID.IsNil()) suite.NotNil(ntsrShipment.DestinationAddressID) diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go index 2ba350e437e..bc2f7ef3c0f 100644 --- a/pkg/gen/ghcapi/embedded_spec.go +++ b/pkg/gen/ghcapi/embedded_spec.go @@ -1911,7 +1911,7 @@ func init() { }, "/move_task_orders/{moveTaskOrderID}/mto_shipments/{shipmentID}": { "patch": { - "description": "Updates a specified MTO shipment.\nRequired fields include:\n* MTO Shipment ID required in path\n* If-Match required in headers\n* No fields required in body\nOptional fields include:\n* New shipment status type\n* Shipment Type\n* Customer requested pick-up date\n* Pick-up Address\n* Delivery Address\n* Secondary Pick-up Address\n* SecondaryDelivery Address\n* Delivery Address Type\n* Customer Remarks\n* Counselor Remarks\n* Releasing / Receiving agents\n* Actual Pro Gear Weight\n* Actual Spouse Pro Gear Weight\n", + "description": "Updates a specified MTO shipment.\nRequired fields include:\n* MTO Shipment ID required in path\n* If-Match required in headers\n* No fields required in body\nOptional fields include:\n* New shipment status type\n* Shipment Type\n* Customer requested pick-up date\n* Pick-up Address\n* Delivery Address\n* Secondary Pick-up Address\n* SecondaryDelivery Address\n* Delivery Address Type\n* Customer Remarks\n* Counselor Remarks\n* Releasing / Receiving agents\n* Actual Pro Gear Weight\n* Actual Spouse Pro Gear Weight\n* Location of the POE/POD\n", "consumes": [ "application/json" ], @@ -7081,6 +7081,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", @@ -9826,6 +9831,12 @@ func init() { "x-nullable": true, "$ref": "#/definitions/Address" }, + "podLocation": { + "$ref": "#/definitions/Port" + }, + "poeLocation": { + "$ref": "#/definitions/Port" + }, "ppmShipment": { "$ref": "#/definitions/PPMShipment" }, @@ -9969,7 +9980,7 @@ func init() { "enum": [ "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "PPM", "BOAT_HAUL_AWAY", "BOAT_TOW_AWAY", @@ -9981,7 +9992,7 @@ func init() { "BOAT_TOW_AWAY": "Boat Tow-Away", "HHG": "HHG", "HHG_INTO_NTS": "NTS", - "HHG_OUTOF_NTS_DOMESTIC": "NTS Release", + "HHG_OUTOF_NTS": "NTS Release", "MOBILE_HOME": "Mobile Home", "PPM": "PPM", "UNACCOMPANIED_BAGGAGE": "Unaccompanied Baggage" @@ -12430,6 +12441,115 @@ func init() { "$ref": "#/definitions/PaymentServiceItem" } }, + "Port": { + "description": "A port that is used to move an international shipment.", + "type": "object", + "properties": { + "city": { + "type": "string", + "example": "PORTLAND" + }, + "country": { + "description": "Two-letter country code", + "type": "string", + "pattern": "^[A-Z]{2}$", + "example": "US" + }, + "county": { + "type": "string", + "example": "MULTNOMAH" + }, + "id": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" + }, + "portCode": { + "description": "3 or 4 digit port code", + "type": "string", + "example": "0431" + }, + "portName": { + "description": "Name of the port", + "type": "string", + "example": "PORTLAND INTL" + }, + "portType": { + "description": "Port type A (Air), B (Border Crossing), S (Sea)", + "type": "string", + "enum": [ + "A", + "B", + "S" + ] + }, + "state": { + "description": "US state", + "type": "string", + "enum": [ + "AL", + "AK", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DC", + "DE", + "FL", + "GA", + "HI", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "MA", + "MD", + "ME", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VA", + "VT", + "WA", + "WI", + "WV", + "WY" + ], + "example": "OR" + }, + "zip": { + "type": "string", + "format": "zip", + "title": "ZIP", + "pattern": "^(\\d{5}([\\-]\\d{4})?)$", + "example": "99501" + } + } + }, "PostDocumentPayload": { "type": "object", "properties": { @@ -12887,7 +13007,7 @@ func init() { "BOAT_TOW_AWAY", "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "MOBILE_HOME", "PPM", "UNACCOMPANIED_BAGGAGE" @@ -17826,7 +17946,7 @@ func init() { }, "/move_task_orders/{moveTaskOrderID}/mto_shipments/{shipmentID}": { "patch": { - "description": "Updates a specified MTO shipment.\nRequired fields include:\n* MTO Shipment ID required in path\n* If-Match required in headers\n* No fields required in body\nOptional fields include:\n* New shipment status type\n* Shipment Type\n* Customer requested pick-up date\n* Pick-up Address\n* Delivery Address\n* Secondary Pick-up Address\n* SecondaryDelivery Address\n* Delivery Address Type\n* Customer Remarks\n* Counselor Remarks\n* Releasing / Receiving agents\n* Actual Pro Gear Weight\n* Actual Spouse Pro Gear Weight\n", + "description": "Updates a specified MTO shipment.\nRequired fields include:\n* MTO Shipment ID required in path\n* If-Match required in headers\n* No fields required in body\nOptional fields include:\n* New shipment status type\n* Shipment Type\n* Customer requested pick-up date\n* Pick-up Address\n* Delivery Address\n* Secondary Pick-up Address\n* SecondaryDelivery Address\n* Delivery Address Type\n* Customer Remarks\n* Counselor Remarks\n* Releasing / Receiving agents\n* Actual Pro Gear Weight\n* Actual Spouse Pro Gear Weight\n* Location of the POE/POD\n", "consumes": [ "application/json" ], @@ -24027,6 +24147,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", @@ -26772,6 +26897,12 @@ func init() { "x-nullable": true, "$ref": "#/definitions/Address" }, + "podLocation": { + "$ref": "#/definitions/Port" + }, + "poeLocation": { + "$ref": "#/definitions/Port" + }, "ppmShipment": { "$ref": "#/definitions/PPMShipment" }, @@ -26915,7 +27046,7 @@ func init() { "enum": [ "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "PPM", "BOAT_HAUL_AWAY", "BOAT_TOW_AWAY", @@ -26927,7 +27058,7 @@ func init() { "BOAT_TOW_AWAY": "Boat Tow-Away", "HHG": "HHG", "HHG_INTO_NTS": "NTS", - "HHG_OUTOF_NTS_DOMESTIC": "NTS Release", + "HHG_OUTOF_NTS": "NTS Release", "MOBILE_HOME": "Mobile Home", "PPM": "PPM", "UNACCOMPANIED_BAGGAGE": "Unaccompanied Baggage" @@ -29450,6 +29581,115 @@ func init() { "$ref": "#/definitions/PaymentServiceItem" } }, + "Port": { + "description": "A port that is used to move an international shipment.", + "type": "object", + "properties": { + "city": { + "type": "string", + "example": "PORTLAND" + }, + "country": { + "description": "Two-letter country code", + "type": "string", + "pattern": "^[A-Z]{2}$", + "example": "US" + }, + "county": { + "type": "string", + "example": "MULTNOMAH" + }, + "id": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" + }, + "portCode": { + "description": "3 or 4 digit port code", + "type": "string", + "example": "0431" + }, + "portName": { + "description": "Name of the port", + "type": "string", + "example": "PORTLAND INTL" + }, + "portType": { + "description": "Port type A (Air), B (Border Crossing), S (Sea)", + "type": "string", + "enum": [ + "A", + "B", + "S" + ] + }, + "state": { + "description": "US state", + "type": "string", + "enum": [ + "AL", + "AK", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DC", + "DE", + "FL", + "GA", + "HI", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "MA", + "MD", + "ME", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VA", + "VT", + "WA", + "WI", + "WV", + "WY" + ], + "example": "OR" + }, + "zip": { + "type": "string", + "format": "zip", + "title": "ZIP", + "pattern": "^(\\d{5}([\\-]\\d{4})?)$", + "example": "99501" + } + } + }, "PostDocumentPayload": { "type": "object", "properties": { @@ -29909,7 +30149,7 @@ func init() { "BOAT_TOW_AWAY", "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "MOBILE_HOME", "PPM", "UNACCOMPANIED_BAGGAGE" diff --git a/pkg/gen/ghcapi/ghcoperations/mto_shipment/update_m_t_o_shipment.go b/pkg/gen/ghcapi/ghcoperations/mto_shipment/update_m_t_o_shipment.go index eb0db5e29ab..2ad855d4d8b 100644 --- a/pkg/gen/ghcapi/ghcoperations/mto_shipment/update_m_t_o_shipment.go +++ b/pkg/gen/ghcapi/ghcoperations/mto_shipment/update_m_t_o_shipment.go @@ -53,6 +53,7 @@ Optional fields include: * Releasing / Receiving agents * Actual Pro Gear Weight * Actual Spouse Pro Gear Weight +* Location of the POE/POD */ type UpdateMTOShipment struct { Context *middleware.Context 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/gen/ghcmessages/m_t_o_shipment.go b/pkg/gen/ghcmessages/m_t_o_shipment.go index 78a6420cee4..793859afdfc 100644 --- a/pkg/gen/ghcmessages/m_t_o_shipment.go +++ b/pkg/gen/ghcmessages/m_t_o_shipment.go @@ -151,6 +151,12 @@ type MTOShipment struct { // pickup address PickupAddress *Address `json:"pickupAddress,omitempty"` + // pod location + PodLocation *Port `json:"podLocation,omitempty"` + + // poe location + PoeLocation *Port `json:"poeLocation,omitempty"` + // ppm shipment PpmShipment *PPMShipment `json:"ppmShipment,omitempty"` @@ -318,6 +324,14 @@ func (m *MTOShipment) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validatePodLocation(formats); err != nil { + res = append(res, err) + } + + if err := m.validatePoeLocation(formats); err != nil { + res = append(res, err) + } + if err := m.validatePpmShipment(formats); err != nil { res = append(res, err) } @@ -698,6 +712,44 @@ func (m *MTOShipment) validatePickupAddress(formats strfmt.Registry) error { return nil } +func (m *MTOShipment) validatePodLocation(formats strfmt.Registry) error { + if swag.IsZero(m.PodLocation) { // not required + return nil + } + + if m.PodLocation != nil { + if err := m.PodLocation.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("podLocation") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("podLocation") + } + return err + } + } + + return nil +} + +func (m *MTOShipment) validatePoeLocation(formats strfmt.Registry) error { + if swag.IsZero(m.PoeLocation) { // not required + return nil + } + + if m.PoeLocation != nil { + if err := m.PoeLocation.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("poeLocation") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("poeLocation") + } + return err + } + } + + return nil +} + func (m *MTOShipment) validatePpmShipment(formats strfmt.Registry) error { if swag.IsZero(m.PpmShipment) { // not required return nil @@ -1051,6 +1103,14 @@ func (m *MTOShipment) ContextValidate(ctx context.Context, formats strfmt.Regist res = append(res, err) } + if err := m.contextValidatePodLocation(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidatePoeLocation(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidatePpmShipment(ctx, formats); err != nil { res = append(res, err) } @@ -1276,6 +1336,48 @@ func (m *MTOShipment) contextValidatePickupAddress(ctx context.Context, formats return nil } +func (m *MTOShipment) contextValidatePodLocation(ctx context.Context, formats strfmt.Registry) error { + + if m.PodLocation != nil { + + if swag.IsZero(m.PodLocation) { // not required + return nil + } + + if err := m.PodLocation.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("podLocation") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("podLocation") + } + return err + } + } + + return nil +} + +func (m *MTOShipment) contextValidatePoeLocation(ctx context.Context, formats strfmt.Registry) error { + + if m.PoeLocation != nil { + + if swag.IsZero(m.PoeLocation) { // not required + return nil + } + + if err := m.PoeLocation.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("poeLocation") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("poeLocation") + } + return err + } + } + + return nil +} + func (m *MTOShipment) contextValidatePpmShipment(ctx context.Context, formats strfmt.Registry) error { if m.PpmShipment != nil { diff --git a/pkg/gen/ghcmessages/m_t_o_shipment_type.go b/pkg/gen/ghcmessages/m_t_o_shipment_type.go index 6e554e7d4d3..0528e2cefb4 100644 --- a/pkg/gen/ghcmessages/m_t_o_shipment_type.go +++ b/pkg/gen/ghcmessages/m_t_o_shipment_type.go @@ -37,8 +37,8 @@ const ( // MTOShipmentTypeHHGINTONTS captures enum value "HHG_INTO_NTS" MTOShipmentTypeHHGINTONTS MTOShipmentType = "HHG_INTO_NTS" - // MTOShipmentTypeHHGOUTOFNTSDOMESTIC captures enum value "HHG_OUTOF_NTS_DOMESTIC" - MTOShipmentTypeHHGOUTOFNTSDOMESTIC MTOShipmentType = "HHG_OUTOF_NTS_DOMESTIC" + // MTOShipmentTypeHHGOUTOFNTS captures enum value "HHG_OUTOF_NTS" + MTOShipmentTypeHHGOUTOFNTS MTOShipmentType = "HHG_OUTOF_NTS" // MTOShipmentTypePPM captures enum value "PPM" MTOShipmentTypePPM MTOShipmentType = "PPM" @@ -61,7 +61,7 @@ var mTOShipmentTypeEnum []interface{} func init() { var res []MTOShipmentType - if err := json.Unmarshal([]byte(`["HHG","HHG_INTO_NTS","HHG_OUTOF_NTS_DOMESTIC","PPM","BOAT_HAUL_AWAY","BOAT_TOW_AWAY","MOBILE_HOME","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["HHG","HHG_INTO_NTS","HHG_OUTOF_NTS","PPM","BOAT_HAUL_AWAY","BOAT_TOW_AWAY","MOBILE_HOME","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/ghcmessages/port.go b/pkg/gen/ghcmessages/port.go new file mode 100644 index 00000000000..5ffb0256db8 --- /dev/null +++ b/pkg/gen/ghcmessages/port.go @@ -0,0 +1,385 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ghcmessages + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// Port A port that is used to move an international shipment. +// +// swagger:model Port +type Port struct { + + // city + // Example: PORTLAND + City string `json:"city,omitempty"` + + // Two-letter country code + // Example: US + // Pattern: ^[A-Z]{2}$ + Country string `json:"country,omitempty"` + + // county + // Example: MULTNOMAH + County string `json:"county,omitempty"` + + // id + // Example: c56a4180-65aa-42ec-a945-5fd21dec0538 + // Format: uuid + ID strfmt.UUID `json:"id,omitempty"` + + // 3 or 4 digit port code + // Example: 0431 + PortCode string `json:"portCode,omitempty"` + + // Name of the port + // Example: PORTLAND INTL + PortName string `json:"portName,omitempty"` + + // Port type A (Air), B (Border Crossing), S (Sea) + // Enum: [A B S] + PortType string `json:"portType,omitempty"` + + // US state + // Example: OR + // Enum: [AL AK AR AZ CA CO CT DC DE FL GA HI IA ID IL IN KS KY LA MA MD ME MI MN MO MS MT NC ND NE NH NJ NM NV NY OH OK OR PA RI SC SD TN TX UT VA VT WA WI WV WY] + State string `json:"state,omitempty"` + + // ZIP + // Example: 99501 + // Pattern: ^(\d{5}([\-]\d{4})?)$ + Zip string `json:"zip,omitempty"` +} + +// Validate validates this port +func (m *Port) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateCountry(formats); err != nil { + res = append(res, err) + } + + if err := m.validateID(formats); err != nil { + res = append(res, err) + } + + if err := m.validatePortType(formats); err != nil { + res = append(res, err) + } + + if err := m.validateState(formats); err != nil { + res = append(res, err) + } + + if err := m.validateZip(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Port) validateCountry(formats strfmt.Registry) error { + if swag.IsZero(m.Country) { // not required + return nil + } + + if err := validate.Pattern("country", "body", m.Country, `^[A-Z]{2}$`); err != nil { + return err + } + + return nil +} + +func (m *Port) 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 +} + +var portTypePortTypePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["A","B","S"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + portTypePortTypePropEnum = append(portTypePortTypePropEnum, v) + } +} + +const ( + + // PortPortTypeA captures enum value "A" + PortPortTypeA string = "A" + + // PortPortTypeB captures enum value "B" + PortPortTypeB string = "B" + + // PortPortTypeS captures enum value "S" + PortPortTypeS string = "S" +) + +// prop value enum +func (m *Port) validatePortTypeEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, portTypePortTypePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *Port) validatePortType(formats strfmt.Registry) error { + if swag.IsZero(m.PortType) { // not required + return nil + } + + // value enum + if err := m.validatePortTypeEnum("portType", "body", m.PortType); err != nil { + return err + } + + return nil +} + +var portTypeStatePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["AL","AK","AR","AZ","CA","CO","CT","DC","DE","FL","GA","HI","IA","ID","IL","IN","KS","KY","LA","MA","MD","ME","MI","MN","MO","MS","MT","NC","ND","NE","NH","NJ","NM","NV","NY","OH","OK","OR","PA","RI","SC","SD","TN","TX","UT","VA","VT","WA","WI","WV","WY"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + portTypeStatePropEnum = append(portTypeStatePropEnum, v) + } +} + +const ( + + // PortStateAL captures enum value "AL" + PortStateAL string = "AL" + + // PortStateAK captures enum value "AK" + PortStateAK string = "AK" + + // PortStateAR captures enum value "AR" + PortStateAR string = "AR" + + // PortStateAZ captures enum value "AZ" + PortStateAZ string = "AZ" + + // PortStateCA captures enum value "CA" + PortStateCA string = "CA" + + // PortStateCO captures enum value "CO" + PortStateCO string = "CO" + + // PortStateCT captures enum value "CT" + PortStateCT string = "CT" + + // PortStateDC captures enum value "DC" + PortStateDC string = "DC" + + // PortStateDE captures enum value "DE" + PortStateDE string = "DE" + + // PortStateFL captures enum value "FL" + PortStateFL string = "FL" + + // PortStateGA captures enum value "GA" + PortStateGA string = "GA" + + // PortStateHI captures enum value "HI" + PortStateHI string = "HI" + + // PortStateIA captures enum value "IA" + PortStateIA string = "IA" + + // PortStateID captures enum value "ID" + PortStateID string = "ID" + + // PortStateIL captures enum value "IL" + PortStateIL string = "IL" + + // PortStateIN captures enum value "IN" + PortStateIN string = "IN" + + // PortStateKS captures enum value "KS" + PortStateKS string = "KS" + + // PortStateKY captures enum value "KY" + PortStateKY string = "KY" + + // PortStateLA captures enum value "LA" + PortStateLA string = "LA" + + // PortStateMA captures enum value "MA" + PortStateMA string = "MA" + + // PortStateMD captures enum value "MD" + PortStateMD string = "MD" + + // PortStateME captures enum value "ME" + PortStateME string = "ME" + + // PortStateMI captures enum value "MI" + PortStateMI string = "MI" + + // PortStateMN captures enum value "MN" + PortStateMN string = "MN" + + // PortStateMO captures enum value "MO" + PortStateMO string = "MO" + + // PortStateMS captures enum value "MS" + PortStateMS string = "MS" + + // PortStateMT captures enum value "MT" + PortStateMT string = "MT" + + // PortStateNC captures enum value "NC" + PortStateNC string = "NC" + + // PortStateND captures enum value "ND" + PortStateND string = "ND" + + // PortStateNE captures enum value "NE" + PortStateNE string = "NE" + + // PortStateNH captures enum value "NH" + PortStateNH string = "NH" + + // PortStateNJ captures enum value "NJ" + PortStateNJ string = "NJ" + + // PortStateNM captures enum value "NM" + PortStateNM string = "NM" + + // PortStateNV captures enum value "NV" + PortStateNV string = "NV" + + // PortStateNY captures enum value "NY" + PortStateNY string = "NY" + + // PortStateOH captures enum value "OH" + PortStateOH string = "OH" + + // PortStateOK captures enum value "OK" + PortStateOK string = "OK" + + // PortStateOR captures enum value "OR" + PortStateOR string = "OR" + + // PortStatePA captures enum value "PA" + PortStatePA string = "PA" + + // PortStateRI captures enum value "RI" + PortStateRI string = "RI" + + // PortStateSC captures enum value "SC" + PortStateSC string = "SC" + + // PortStateSD captures enum value "SD" + PortStateSD string = "SD" + + // PortStateTN captures enum value "TN" + PortStateTN string = "TN" + + // PortStateTX captures enum value "TX" + PortStateTX string = "TX" + + // PortStateUT captures enum value "UT" + PortStateUT string = "UT" + + // PortStateVA captures enum value "VA" + PortStateVA string = "VA" + + // PortStateVT captures enum value "VT" + PortStateVT string = "VT" + + // PortStateWA captures enum value "WA" + PortStateWA string = "WA" + + // PortStateWI captures enum value "WI" + PortStateWI string = "WI" + + // PortStateWV captures enum value "WV" + PortStateWV string = "WV" + + // PortStateWY captures enum value "WY" + PortStateWY string = "WY" +) + +// prop value enum +func (m *Port) validateStateEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, portTypeStatePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *Port) validateState(formats strfmt.Registry) error { + if swag.IsZero(m.State) { // not required + return nil + } + + // value enum + if err := m.validateStateEnum("state", "body", m.State); err != nil { + return err + } + + return nil +} + +func (m *Port) validateZip(formats strfmt.Registry) error { + if swag.IsZero(m.Zip) { // not required + return nil + } + + if err := validate.Pattern("zip", "body", m.Zip, `^(\d{5}([\-]\d{4})?)$`); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this port based on context it is used +func (m *Port) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *Port) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Port) UnmarshalBinary(b []byte) error { + var res Port + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/gen/ghcmessages/re_service_item.go b/pkg/gen/ghcmessages/re_service_item.go index f90165b8924..9dd0a8d13e1 100644 --- a/pkg/gen/ghcmessages/re_service_item.go +++ b/pkg/gen/ghcmessages/re_service_item.go @@ -40,7 +40,7 @@ type ReServiceItem struct { // shipment type // Example: HHG, UNACCOMPANIED_BAGGAGE - // Enum: [BOAT_HAUL_AWAY BOAT_TOW_AWAY HHG HHG_INTO_NTS HHG_OUTOF_NTS_DOMESTIC MOBILE_HOME PPM UNACCOMPANIED_BAGGAGE] + // Enum: [BOAT_HAUL_AWAY BOAT_TOW_AWAY HHG HHG_INTO_NTS HHG_OUTOF_NTS MOBILE_HOME PPM UNACCOMPANIED_BAGGAGE] ShipmentType string `json:"shipmentType,omitempty"` } @@ -298,7 +298,7 @@ var reServiceItemTypeShipmentTypePropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["BOAT_HAUL_AWAY","BOAT_TOW_AWAY","HHG","HHG_INTO_NTS","HHG_OUTOF_NTS_DOMESTIC","MOBILE_HOME","PPM","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["BOAT_HAUL_AWAY","BOAT_TOW_AWAY","HHG","HHG_INTO_NTS","HHG_OUTOF_NTS","MOBILE_HOME","PPM","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -320,8 +320,8 @@ const ( // ReServiceItemShipmentTypeHHGINTONTS captures enum value "HHG_INTO_NTS" ReServiceItemShipmentTypeHHGINTONTS string = "HHG_INTO_NTS" - // ReServiceItemShipmentTypeHHGOUTOFNTSDOMESTIC captures enum value "HHG_OUTOF_NTS_DOMESTIC" - ReServiceItemShipmentTypeHHGOUTOFNTSDOMESTIC string = "HHG_OUTOF_NTS_DOMESTIC" + // ReServiceItemShipmentTypeHHGOUTOFNTS captures enum value "HHG_OUTOF_NTS" + ReServiceItemShipmentTypeHHGOUTOFNTS string = "HHG_OUTOF_NTS" // ReServiceItemShipmentTypeMOBILEHOME captures enum value "MOBILE_HOME" ReServiceItemShipmentTypeMOBILEHOME string = "MOBILE_HOME" diff --git a/pkg/gen/internalapi/embedded_spec.go b/pkg/gen/internalapi/embedded_spec.go index 0e198c6c4e6..96f02d9b142 100644 --- a/pkg/gen/internalapi/embedded_spec.go +++ b/pkg/gen/internalapi/embedded_spec.go @@ -4946,7 +4946,7 @@ func init() { "enum": [ "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "PPM", "BOAT_HAUL_AWAY", "BOAT_TOW_AWAY", @@ -4958,7 +4958,7 @@ func init() { "BOAT_TOW_AWAY": "Boat Tow-Away", "HHG": "HHG", "HHG_INTO_NTS": "NTS", - "HHG_OUTOF_NTS_DOMESTIC": "NTS Release", + "HHG_OUTOF_NTS": "NTS Release", "MOBILE_HOME": "Mobile Home", "PPM": "PPM", "UNACCOMPANIED_BAGGAGE": "Unaccompanied Baggage" @@ -14072,7 +14072,7 @@ func init() { "enum": [ "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "PPM", "BOAT_HAUL_AWAY", "BOAT_TOW_AWAY", @@ -14084,7 +14084,7 @@ func init() { "BOAT_TOW_AWAY": "Boat Tow-Away", "HHG": "HHG", "HHG_INTO_NTS": "NTS", - "HHG_OUTOF_NTS_DOMESTIC": "NTS Release", + "HHG_OUTOF_NTS": "NTS Release", "MOBILE_HOME": "Mobile Home", "PPM": "PPM", "UNACCOMPANIED_BAGGAGE": "Unaccompanied Baggage" diff --git a/pkg/gen/internalmessages/m_t_o_shipment_type.go b/pkg/gen/internalmessages/m_t_o_shipment_type.go index a3853e1f64c..bf5b8189f99 100644 --- a/pkg/gen/internalmessages/m_t_o_shipment_type.go +++ b/pkg/gen/internalmessages/m_t_o_shipment_type.go @@ -37,8 +37,8 @@ const ( // MTOShipmentTypeHHGINTONTS captures enum value "HHG_INTO_NTS" MTOShipmentTypeHHGINTONTS MTOShipmentType = "HHG_INTO_NTS" - // MTOShipmentTypeHHGOUTOFNTSDOMESTIC captures enum value "HHG_OUTOF_NTS_DOMESTIC" - MTOShipmentTypeHHGOUTOFNTSDOMESTIC MTOShipmentType = "HHG_OUTOF_NTS_DOMESTIC" + // MTOShipmentTypeHHGOUTOFNTS captures enum value "HHG_OUTOF_NTS" + MTOShipmentTypeHHGOUTOFNTS MTOShipmentType = "HHG_OUTOF_NTS" // MTOShipmentTypePPM captures enum value "PPM" MTOShipmentTypePPM MTOShipmentType = "PPM" @@ -61,7 +61,7 @@ var mTOShipmentTypeEnum []interface{} func init() { var res []MTOShipmentType - if err := json.Unmarshal([]byte(`["HHG","HHG_INTO_NTS","HHG_OUTOF_NTS_DOMESTIC","PPM","BOAT_HAUL_AWAY","BOAT_TOW_AWAY","MOBILE_HOME","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["HHG","HHG_INTO_NTS","HHG_OUTOF_NTS","PPM","BOAT_HAUL_AWAY","BOAT_TOW_AWAY","MOBILE_HOME","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/primeapi/embedded_spec.go b/pkg/gen/primeapi/embedded_spec.go index 5f3561c66d6..78a49944297 100644 --- a/pkg/gen/primeapi/embedded_spec.go +++ b/pkg/gen/primeapi/embedded_spec.go @@ -2555,7 +2555,7 @@ func init() { } }, "MTOShipmentType": { - "description": "The type of shipment.\n * ` + "`" + `HHG` + "`" + ` = Household goods move\n * ` + "`" + `HHG_INTO_NTS` + "`" + ` = HHG into Non-temporary storage (NTS)\n * ` + "`" + `HHG_OUTOF_NTS_DOMESTIC` + "`" + ` = HHG out of Non-temporary storage (NTS Release)\n * ` + "`" + `PPM` + "`" + ` = Personally Procured Move also known as Do It Yourself (DITY)\n * ` + "`" + `BOAT_HAUL_AWAY` + "`" + ` = Boat shipment that requires additional equipment to haul it to it's destination\n * ` + "`" + `BOAT_TOW_AWAY` + "`" + ` = Boat shipment that has a road-worthy trailer\n * ` + "`" + `MOBILE_HOME` + "`" + ` = Mobile Home shipment that a customer may move.\n", + "description": "The type of shipment.\n * ` + "`" + `HHG` + "`" + ` = Household goods move\n * ` + "`" + `HHG_INTO_NTS` + "`" + ` = HHG into Non-temporary storage (NTS)\n * ` + "`" + `HHG_OUTOF_NTS` + "`" + ` = HHG out of Non-temporary storage (NTS Release)\n * ` + "`" + `PPM` + "`" + ` = Personally Procured Move also known as Do It Yourself (DITY)\n * ` + "`" + `BOAT_HAUL_AWAY` + "`" + ` = Boat shipment that requires additional equipment to haul it to it's destination\n * ` + "`" + `BOAT_TOW_AWAY` + "`" + ` = Boat shipment that has a road-worthy trailer\n * ` + "`" + `MOBILE_HOME` + "`" + ` = Mobile Home shipment that a customer may move.\n", "type": "string", "title": "Shipment Type", "enum": [ @@ -2563,7 +2563,7 @@ func init() { "BOAT_TOW_AWAY", "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "MOBILE_HOME", "PPM", "UNACCOMPANIED_BAGGAGE" @@ -2573,7 +2573,7 @@ func init() { "BOAT_TOW_AWAY": "Boat shipment that has a road-worthy trailer", "HHG": "Household goods move (HHG)", "HHG_INTO_NTS": "HHG into Non-temporary storage (NTS)", - "HHG_OUTOF_NTS_DOMESTIC": "HHG out of Non-temporary storage (NTS Release)", + "HHG_OUTOF_NTS": "HHG out of Non-temporary storage (NTS Release)", "PPM": "Personally Procured Move also known as Do It Yourself (DITY)", "UNACCOMPANIED_BAGGAGE": "Unaccompanied Baggage" }, @@ -7504,7 +7504,7 @@ func init() { } }, "MTOShipmentType": { - "description": "The type of shipment.\n * ` + "`" + `HHG` + "`" + ` = Household goods move\n * ` + "`" + `HHG_INTO_NTS` + "`" + ` = HHG into Non-temporary storage (NTS)\n * ` + "`" + `HHG_OUTOF_NTS_DOMESTIC` + "`" + ` = HHG out of Non-temporary storage (NTS Release)\n * ` + "`" + `PPM` + "`" + ` = Personally Procured Move also known as Do It Yourself (DITY)\n * ` + "`" + `BOAT_HAUL_AWAY` + "`" + ` = Boat shipment that requires additional equipment to haul it to it's destination\n * ` + "`" + `BOAT_TOW_AWAY` + "`" + ` = Boat shipment that has a road-worthy trailer\n * ` + "`" + `MOBILE_HOME` + "`" + ` = Mobile Home shipment that a customer may move.\n", + "description": "The type of shipment.\n * ` + "`" + `HHG` + "`" + ` = Household goods move\n * ` + "`" + `HHG_INTO_NTS` + "`" + ` = HHG into Non-temporary storage (NTS)\n * ` + "`" + `HHG_OUTOF_NTS` + "`" + ` = HHG out of Non-temporary storage (NTS Release)\n * ` + "`" + `PPM` + "`" + ` = Personally Procured Move also known as Do It Yourself (DITY)\n * ` + "`" + `BOAT_HAUL_AWAY` + "`" + ` = Boat shipment that requires additional equipment to haul it to it's destination\n * ` + "`" + `BOAT_TOW_AWAY` + "`" + ` = Boat shipment that has a road-worthy trailer\n * ` + "`" + `MOBILE_HOME` + "`" + ` = Mobile Home shipment that a customer may move.\n", "type": "string", "title": "Shipment Type", "enum": [ @@ -7512,7 +7512,7 @@ func init() { "BOAT_TOW_AWAY", "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "MOBILE_HOME", "PPM", "UNACCOMPANIED_BAGGAGE" @@ -7522,7 +7522,7 @@ func init() { "BOAT_TOW_AWAY": "Boat shipment that has a road-worthy trailer", "HHG": "Household goods move (HHG)", "HHG_INTO_NTS": "HHG into Non-temporary storage (NTS)", - "HHG_OUTOF_NTS_DOMESTIC": "HHG out of Non-temporary storage (NTS Release)", + "HHG_OUTOF_NTS": "HHG out of Non-temporary storage (NTS Release)", "PPM": "Personally Procured Move also known as Do It Yourself (DITY)", "UNACCOMPANIED_BAGGAGE": "Unaccompanied Baggage" }, diff --git a/pkg/gen/primemessages/m_t_o_shipment_type.go b/pkg/gen/primemessages/m_t_o_shipment_type.go index 45ce8a14201..14277b66897 100644 --- a/pkg/gen/primemessages/m_t_o_shipment_type.go +++ b/pkg/gen/primemessages/m_t_o_shipment_type.go @@ -19,7 +19,7 @@ import ( // The type of shipment. // - `HHG` = Household goods move // - `HHG_INTO_NTS` = HHG into Non-temporary storage (NTS) -// - `HHG_OUTOF_NTS_DOMESTIC` = HHG out of Non-temporary storage (NTS Release) +// - `HHG_OUTOF_NTS` = HHG out of Non-temporary storage (NTS Release) // - `PPM` = Personally Procured Move also known as Do It Yourself (DITY) // - `BOAT_HAUL_AWAY` = Boat shipment that requires additional equipment to haul it to it's destination // - `BOAT_TOW_AWAY` = Boat shipment that has a road-worthy trailer @@ -53,8 +53,8 @@ const ( // MTOShipmentTypeHHGINTONTS captures enum value "HHG_INTO_NTS" MTOShipmentTypeHHGINTONTS MTOShipmentType = "HHG_INTO_NTS" - // MTOShipmentTypeHHGOUTOFNTSDOMESTIC captures enum value "HHG_OUTOF_NTS_DOMESTIC" - MTOShipmentTypeHHGOUTOFNTSDOMESTIC MTOShipmentType = "HHG_OUTOF_NTS_DOMESTIC" + // MTOShipmentTypeHHGOUTOFNTS captures enum value "HHG_OUTOF_NTS" + MTOShipmentTypeHHGOUTOFNTS MTOShipmentType = "HHG_OUTOF_NTS" // MTOShipmentTypeMOBILEHOME captures enum value "MOBILE_HOME" MTOShipmentTypeMOBILEHOME MTOShipmentType = "MOBILE_HOME" @@ -71,7 +71,7 @@ var mTOShipmentTypeEnum []interface{} func init() { var res []MTOShipmentType - if err := json.Unmarshal([]byte(`["BOAT_HAUL_AWAY","BOAT_TOW_AWAY","HHG","HHG_INTO_NTS","HHG_OUTOF_NTS_DOMESTIC","MOBILE_HOME","PPM","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["BOAT_HAUL_AWAY","BOAT_TOW_AWAY","HHG","HHG_INTO_NTS","HHG_OUTOF_NTS","MOBILE_HOME","PPM","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/primev2api/embedded_spec.go b/pkg/gen/primev2api/embedded_spec.go index 74ee52d88c1..2d563e13644 100644 --- a/pkg/gen/primev2api/embedded_spec.go +++ b/pkg/gen/primev2api/embedded_spec.go @@ -186,7 +186,7 @@ func init() { } ], "moveTaskOrderId": "5691c951-c35c-49a8-a1d5-a4b7ea7b7ad8", - "shipmentType": "HHG_OUTOF_NTS_DOMESTIC" + "shipmentType": "HHG_OUTOF_NTS" } }, "ppm": { @@ -1635,7 +1635,7 @@ func init() { } }, "MTOShipmentType": { - "description": "The type of shipment.\n * ` + "`" + `HHG` + "`" + ` = Household goods move\n * ` + "`" + `HHG_INTO_NTS` + "`" + ` = HHG into Non-temporary storage (NTS)\n * ` + "`" + `HHG_OUTOF_NTS_DOMESTIC` + "`" + ` = HHG out of Non-temporary storage (NTS Release)\n * ` + "`" + `PPM` + "`" + ` = Personally Procured Move also known as Do It Yourself (DITY)\n * ` + "`" + `BOAT_HAUL_AWAY` + "`" + ` = Boat shipment that requires additional equipment to haul it to it's destination\n * ` + "`" + `BOAT_TOW_AWAY` + "`" + ` = Boat shipment that has a road-worthy trailer\n * ` + "`" + `MOBILE_HOME` + "`" + ` = Mobile Home shipment that a customer may move.\n", + "description": "The type of shipment.\n * ` + "`" + `HHG` + "`" + ` = Household goods move\n * ` + "`" + `HHG_INTO_NTS` + "`" + ` = HHG into Non-temporary storage (NTS)\n * ` + "`" + `HHG_OUTOF_NTS` + "`" + ` = HHG out of Non-temporary storage (NTS Release)\n * ` + "`" + `PPM` + "`" + ` = Personally Procured Move also known as Do It Yourself (DITY)\n * ` + "`" + `BOAT_HAUL_AWAY` + "`" + ` = Boat shipment that requires additional equipment to haul it to it's destination\n * ` + "`" + `BOAT_TOW_AWAY` + "`" + ` = Boat shipment that has a road-worthy trailer\n * ` + "`" + `MOBILE_HOME` + "`" + ` = Mobile Home shipment that a customer may move.\n", "type": "string", "title": "Shipment Type", "enum": [ @@ -1643,7 +1643,7 @@ func init() { "BOAT_TOW_AWAY", "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "MOBILE_HOME", "PPM", "UNACCOMPANIED_BAGGAGE" @@ -1653,7 +1653,7 @@ func init() { "BOAT_TOW_AWAY": "Boat shipment that has a road-worthy trailer", "HHG": "Household goods move (HHG)", "HHG_INTO_NTS": "HHG into Non-temporary storage (NTS)", - "HHG_OUTOF_NTS_DOMESTIC": "HHG out of Non-temporary storage (NTS Release)", + "HHG_OUTOF_NTS": "HHG out of Non-temporary storage (NTS Release)", "PPM": "Personally Procured Move also known as Do It Yourself (DITY)", "UNACCOMPANIED_BAGGAGE": "Unaccompanied Baggage" }, @@ -3785,7 +3785,7 @@ func init() { } ], "moveTaskOrderId": "5691c951-c35c-49a8-a1d5-a4b7ea7b7ad8", - "shipmentType": "HHG_OUTOF_NTS_DOMESTIC" + "shipmentType": "HHG_OUTOF_NTS" } }, "ppm": { @@ -5271,7 +5271,7 @@ func init() { } }, "MTOShipmentType": { - "description": "The type of shipment.\n * ` + "`" + `HHG` + "`" + ` = Household goods move\n * ` + "`" + `HHG_INTO_NTS` + "`" + ` = HHG into Non-temporary storage (NTS)\n * ` + "`" + `HHG_OUTOF_NTS_DOMESTIC` + "`" + ` = HHG out of Non-temporary storage (NTS Release)\n * ` + "`" + `PPM` + "`" + ` = Personally Procured Move also known as Do It Yourself (DITY)\n * ` + "`" + `BOAT_HAUL_AWAY` + "`" + ` = Boat shipment that requires additional equipment to haul it to it's destination\n * ` + "`" + `BOAT_TOW_AWAY` + "`" + ` = Boat shipment that has a road-worthy trailer\n * ` + "`" + `MOBILE_HOME` + "`" + ` = Mobile Home shipment that a customer may move.\n", + "description": "The type of shipment.\n * ` + "`" + `HHG` + "`" + ` = Household goods move\n * ` + "`" + `HHG_INTO_NTS` + "`" + ` = HHG into Non-temporary storage (NTS)\n * ` + "`" + `HHG_OUTOF_NTS` + "`" + ` = HHG out of Non-temporary storage (NTS Release)\n * ` + "`" + `PPM` + "`" + ` = Personally Procured Move also known as Do It Yourself (DITY)\n * ` + "`" + `BOAT_HAUL_AWAY` + "`" + ` = Boat shipment that requires additional equipment to haul it to it's destination\n * ` + "`" + `BOAT_TOW_AWAY` + "`" + ` = Boat shipment that has a road-worthy trailer\n * ` + "`" + `MOBILE_HOME` + "`" + ` = Mobile Home shipment that a customer may move.\n", "type": "string", "title": "Shipment Type", "enum": [ @@ -5279,7 +5279,7 @@ func init() { "BOAT_TOW_AWAY", "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "MOBILE_HOME", "PPM", "UNACCOMPANIED_BAGGAGE" @@ -5289,7 +5289,7 @@ func init() { "BOAT_TOW_AWAY": "Boat shipment that has a road-worthy trailer", "HHG": "Household goods move (HHG)", "HHG_INTO_NTS": "HHG into Non-temporary storage (NTS)", - "HHG_OUTOF_NTS_DOMESTIC": "HHG out of Non-temporary storage (NTS Release)", + "HHG_OUTOF_NTS": "HHG out of Non-temporary storage (NTS Release)", "PPM": "Personally Procured Move also known as Do It Yourself (DITY)", "UNACCOMPANIED_BAGGAGE": "Unaccompanied Baggage" }, diff --git a/pkg/gen/primev2messages/m_t_o_shipment_type.go b/pkg/gen/primev2messages/m_t_o_shipment_type.go index 79fe1f1ef90..64f3918f316 100644 --- a/pkg/gen/primev2messages/m_t_o_shipment_type.go +++ b/pkg/gen/primev2messages/m_t_o_shipment_type.go @@ -19,7 +19,7 @@ import ( // The type of shipment. // - `HHG` = Household goods move // - `HHG_INTO_NTS` = HHG into Non-temporary storage (NTS) -// - `HHG_OUTOF_NTS_DOMESTIC` = HHG out of Non-temporary storage (NTS Release) +// - `HHG_OUTOF_NTS` = HHG out of Non-temporary storage (NTS Release) // - `PPM` = Personally Procured Move also known as Do It Yourself (DITY) // - `BOAT_HAUL_AWAY` = Boat shipment that requires additional equipment to haul it to it's destination // - `BOAT_TOW_AWAY` = Boat shipment that has a road-worthy trailer @@ -53,8 +53,8 @@ const ( // MTOShipmentTypeHHGINTONTS captures enum value "HHG_INTO_NTS" MTOShipmentTypeHHGINTONTS MTOShipmentType = "HHG_INTO_NTS" - // MTOShipmentTypeHHGOUTOFNTSDOMESTIC captures enum value "HHG_OUTOF_NTS_DOMESTIC" - MTOShipmentTypeHHGOUTOFNTSDOMESTIC MTOShipmentType = "HHG_OUTOF_NTS_DOMESTIC" + // MTOShipmentTypeHHGOUTOFNTS captures enum value "HHG_OUTOF_NTS" + MTOShipmentTypeHHGOUTOFNTS MTOShipmentType = "HHG_OUTOF_NTS" // MTOShipmentTypeMOBILEHOME captures enum value "MOBILE_HOME" MTOShipmentTypeMOBILEHOME MTOShipmentType = "MOBILE_HOME" @@ -71,7 +71,7 @@ var mTOShipmentTypeEnum []interface{} func init() { var res []MTOShipmentType - if err := json.Unmarshal([]byte(`["BOAT_HAUL_AWAY","BOAT_TOW_AWAY","HHG","HHG_INTO_NTS","HHG_OUTOF_NTS_DOMESTIC","MOBILE_HOME","PPM","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["BOAT_HAUL_AWAY","BOAT_TOW_AWAY","HHG","HHG_INTO_NTS","HHG_OUTOF_NTS","MOBILE_HOME","PPM","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/primev3api/embedded_spec.go b/pkg/gen/primev3api/embedded_spec.go index b35aaf504b1..147c3232d19 100644 --- a/pkg/gen/primev3api/embedded_spec.go +++ b/pkg/gen/primev3api/embedded_spec.go @@ -186,7 +186,7 @@ func init() { } ], "moveTaskOrderId": "5691c951-c35c-49a8-a1d5-a4b7ea7b7ad8", - "shipmentType": "HHG_OUTOF_NTS_DOMESTIC" + "shipmentType": "HHG_OUTOF_NTS" } }, "ppm": { @@ -1822,7 +1822,7 @@ func init() { } }, "MTOShipmentType": { - "description": "The type of shipment.\n * ` + "`" + `HHG` + "`" + ` = Household goods move\n * ` + "`" + `HHG_INTO_NTS` + "`" + ` = HHG into Non-temporary storage (NTS)\n * ` + "`" + `HHG_OUTOF_NTS_DOMESTIC` + "`" + ` = HHG out of Non-temporary storage (NTS Release)\n * ` + "`" + `PPM` + "`" + ` = Personally Procured Move also known as Do It Yourself (DITY)\n * ` + "`" + `BOAT_HAUL_AWAY` + "`" + ` = Boat shipment that requires additional equipment to haul it to it's destination\n * ` + "`" + `BOAT_TOW_AWAY` + "`" + ` = Boat shipment that has a road-worthy trailer\n * ` + "`" + `MOBILE_HOME` + "`" + ` = Mobile Home shipment that a customer may move.\n", + "description": "The type of shipment.\n * ` + "`" + `HHG` + "`" + ` = Household goods move\n * ` + "`" + `HHG_INTO_NTS` + "`" + ` = HHG into Non-temporary storage (NTS)\n * ` + "`" + `HHG_OUTOF_NTS` + "`" + ` = HHG out of Non-temporary storage (NTS Release)\n * ` + "`" + `PPM` + "`" + ` = Personally Procured Move also known as Do It Yourself (DITY)\n * ` + "`" + `BOAT_HAUL_AWAY` + "`" + ` = Boat shipment that requires additional equipment to haul it to it's destination\n * ` + "`" + `BOAT_TOW_AWAY` + "`" + ` = Boat shipment that has a road-worthy trailer\n * ` + "`" + `MOBILE_HOME` + "`" + ` = Mobile Home shipment that a customer may move.\n", "type": "string", "title": "Shipment Type", "enum": [ @@ -1830,7 +1830,7 @@ func init() { "BOAT_TOW_AWAY", "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "MOBILE_HOME", "PPM", "UNACCOMPANIED_BAGGAGE" @@ -1840,7 +1840,7 @@ func init() { "BOAT_TOW_AWAY": "Boat shipment that has a road-worthy trailer", "HHG": "Household goods move (HHG)", "HHG_INTO_NTS": "HHG into Non-temporary storage (NTS)", - "HHG_OUTOF_NTS_DOMESTIC": "HHG out of Non-temporary storage (NTS Release)", + "HHG_OUTOF_NTS": "HHG out of Non-temporary storage (NTS Release)", "PPM": "Personally Procured Move also known as Do It Yourself (DITY)", "UNACCOMPANIED_BAGGAGE": "Unaccompanied Baggage" }, @@ -4495,7 +4495,7 @@ func init() { } ], "moveTaskOrderId": "5691c951-c35c-49a8-a1d5-a4b7ea7b7ad8", - "shipmentType": "HHG_OUTOF_NTS_DOMESTIC" + "shipmentType": "HHG_OUTOF_NTS" } }, "ppm": { @@ -6168,7 +6168,7 @@ func init() { } }, "MTOShipmentType": { - "description": "The type of shipment.\n * ` + "`" + `HHG` + "`" + ` = Household goods move\n * ` + "`" + `HHG_INTO_NTS` + "`" + ` = HHG into Non-temporary storage (NTS)\n * ` + "`" + `HHG_OUTOF_NTS_DOMESTIC` + "`" + ` = HHG out of Non-temporary storage (NTS Release)\n * ` + "`" + `PPM` + "`" + ` = Personally Procured Move also known as Do It Yourself (DITY)\n * ` + "`" + `BOAT_HAUL_AWAY` + "`" + ` = Boat shipment that requires additional equipment to haul it to it's destination\n * ` + "`" + `BOAT_TOW_AWAY` + "`" + ` = Boat shipment that has a road-worthy trailer\n * ` + "`" + `MOBILE_HOME` + "`" + ` = Mobile Home shipment that a customer may move.\n", + "description": "The type of shipment.\n * ` + "`" + `HHG` + "`" + ` = Household goods move\n * ` + "`" + `HHG_INTO_NTS` + "`" + ` = HHG into Non-temporary storage (NTS)\n * ` + "`" + `HHG_OUTOF_NTS` + "`" + ` = HHG out of Non-temporary storage (NTS Release)\n * ` + "`" + `PPM` + "`" + ` = Personally Procured Move also known as Do It Yourself (DITY)\n * ` + "`" + `BOAT_HAUL_AWAY` + "`" + ` = Boat shipment that requires additional equipment to haul it to it's destination\n * ` + "`" + `BOAT_TOW_AWAY` + "`" + ` = Boat shipment that has a road-worthy trailer\n * ` + "`" + `MOBILE_HOME` + "`" + ` = Mobile Home shipment that a customer may move.\n", "type": "string", "title": "Shipment Type", "enum": [ @@ -6176,7 +6176,7 @@ func init() { "BOAT_TOW_AWAY", "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "MOBILE_HOME", "PPM", "UNACCOMPANIED_BAGGAGE" @@ -6186,7 +6186,7 @@ func init() { "BOAT_TOW_AWAY": "Boat shipment that has a road-worthy trailer", "HHG": "Household goods move (HHG)", "HHG_INTO_NTS": "HHG into Non-temporary storage (NTS)", - "HHG_OUTOF_NTS_DOMESTIC": "HHG out of Non-temporary storage (NTS Release)", + "HHG_OUTOF_NTS": "HHG out of Non-temporary storage (NTS Release)", "PPM": "Personally Procured Move also known as Do It Yourself (DITY)", "UNACCOMPANIED_BAGGAGE": "Unaccompanied Baggage" }, diff --git a/pkg/gen/primev3messages/m_t_o_shipment_type.go b/pkg/gen/primev3messages/m_t_o_shipment_type.go index cbcda952606..ec1aaf63d24 100644 --- a/pkg/gen/primev3messages/m_t_o_shipment_type.go +++ b/pkg/gen/primev3messages/m_t_o_shipment_type.go @@ -19,7 +19,7 @@ import ( // The type of shipment. // - `HHG` = Household goods move // - `HHG_INTO_NTS` = HHG into Non-temporary storage (NTS) -// - `HHG_OUTOF_NTS_DOMESTIC` = HHG out of Non-temporary storage (NTS Release) +// - `HHG_OUTOF_NTS` = HHG out of Non-temporary storage (NTS Release) // - `PPM` = Personally Procured Move also known as Do It Yourself (DITY) // - `BOAT_HAUL_AWAY` = Boat shipment that requires additional equipment to haul it to it's destination // - `BOAT_TOW_AWAY` = Boat shipment that has a road-worthy trailer @@ -53,8 +53,8 @@ const ( // MTOShipmentTypeHHGINTONTS captures enum value "HHG_INTO_NTS" MTOShipmentTypeHHGINTONTS MTOShipmentType = "HHG_INTO_NTS" - // MTOShipmentTypeHHGOUTOFNTSDOMESTIC captures enum value "HHG_OUTOF_NTS_DOMESTIC" - MTOShipmentTypeHHGOUTOFNTSDOMESTIC MTOShipmentType = "HHG_OUTOF_NTS_DOMESTIC" + // MTOShipmentTypeHHGOUTOFNTS captures enum value "HHG_OUTOF_NTS" + MTOShipmentTypeHHGOUTOFNTS MTOShipmentType = "HHG_OUTOF_NTS" // MTOShipmentTypeMOBILEHOME captures enum value "MOBILE_HOME" MTOShipmentTypeMOBILEHOME MTOShipmentType = "MOBILE_HOME" @@ -71,7 +71,7 @@ var mTOShipmentTypeEnum []interface{} func init() { var res []MTOShipmentType - if err := json.Unmarshal([]byte(`["BOAT_HAUL_AWAY","BOAT_TOW_AWAY","HHG","HHG_INTO_NTS","HHG_OUTOF_NTS_DOMESTIC","MOBILE_HOME","PPM","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["BOAT_HAUL_AWAY","BOAT_TOW_AWAY","HHG","HHG_INTO_NTS","HHG_OUTOF_NTS","MOBILE_HOME","PPM","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/supportapi/embedded_spec.go b/pkg/gen/supportapi/embedded_spec.go index 65ee2ddea96..63314742252 100644 --- a/pkg/gen/supportapi/embedded_spec.go +++ b/pkg/gen/supportapi/embedded_spec.go @@ -1793,7 +1793,7 @@ func init() { "enum": [ "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "PPM", "BOAT_HAUL_AWAY", "BOAT_TOW_AWAY", @@ -1805,7 +1805,7 @@ func init() { "BOAT_TOW_AWAY": "Boat Tow-Away", "HHG": "HHG", "HHG_INTO_NTS": "NTS", - "HHG_OUTOF_NTS_DOMESTIC": "NTS Release", + "HHG_OUTOF_NTS": "NTS Release", "MOBILE_HOME": "Mobile Home", "PPM": "PPM", "UNACCOMPANIED_BAGGAGE": "Unaccompanied Baggage" @@ -4660,7 +4660,7 @@ func init() { "enum": [ "HHG", "HHG_INTO_NTS", - "HHG_OUTOF_NTS_DOMESTIC", + "HHG_OUTOF_NTS", "PPM", "BOAT_HAUL_AWAY", "BOAT_TOW_AWAY", @@ -4672,7 +4672,7 @@ func init() { "BOAT_TOW_AWAY": "Boat Tow-Away", "HHG": "HHG", "HHG_INTO_NTS": "NTS", - "HHG_OUTOF_NTS_DOMESTIC": "NTS Release", + "HHG_OUTOF_NTS": "NTS Release", "MOBILE_HOME": "Mobile Home", "PPM": "PPM", "UNACCOMPANIED_BAGGAGE": "Unaccompanied Baggage" diff --git a/pkg/gen/supportmessages/m_t_o_shipment_type.go b/pkg/gen/supportmessages/m_t_o_shipment_type.go index 1fb19e022f8..d5059f70f83 100644 --- a/pkg/gen/supportmessages/m_t_o_shipment_type.go +++ b/pkg/gen/supportmessages/m_t_o_shipment_type.go @@ -37,8 +37,8 @@ const ( // MTOShipmentTypeHHGINTONTS captures enum value "HHG_INTO_NTS" MTOShipmentTypeHHGINTONTS MTOShipmentType = "HHG_INTO_NTS" - // MTOShipmentTypeHHGOUTOFNTSDOMESTIC captures enum value "HHG_OUTOF_NTS_DOMESTIC" - MTOShipmentTypeHHGOUTOFNTSDOMESTIC MTOShipmentType = "HHG_OUTOF_NTS_DOMESTIC" + // MTOShipmentTypeHHGOUTOFNTS captures enum value "HHG_OUTOF_NTS" + MTOShipmentTypeHHGOUTOFNTS MTOShipmentType = "HHG_OUTOF_NTS" // MTOShipmentTypePPM captures enum value "PPM" MTOShipmentTypePPM MTOShipmentType = "PPM" @@ -61,7 +61,7 @@ var mTOShipmentTypeEnum []interface{} func init() { var res []MTOShipmentType - if err := json.Unmarshal([]byte(`["HHG","HHG_INTO_NTS","HHG_OUTOF_NTS_DOMESTIC","PPM","BOAT_HAUL_AWAY","BOAT_TOW_AWAY","MOBILE_HOME","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["HHG","HHG_INTO_NTS","HHG_OUTOF_NTS","PPM","BOAT_HAUL_AWAY","BOAT_TOW_AWAY","MOBILE_HOME","UNACCOMPANIED_BAGGAGE"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index 87a7517ded5..44c208f97e9 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -682,6 +682,7 @@ func Order(order *models.Order) *ghcmessages.Order { MoveCode: moveCode, MoveTaskOrderID: moveTaskOrderID, OriginDutyLocationGBLOC: ghcmessages.GBLOC(swag.StringValue(order.OriginDutyLocationGBLOC)), + HasDependents: order.HasDependents, } return &payload @@ -1526,6 +1527,8 @@ func MTOShipment(storer storage.FileStorer, mtoShipment *models.MTOShipment, sit DeliveryAddressUpdate: ShipmentAddressUpdate(mtoShipment.DeliveryAddressUpdate), ShipmentLocator: handlers.FmtStringPtr(mtoShipment.ShipmentLocator), MarketCode: MarketCode(&mtoShipment.MarketCode), + PoeLocation: Port(mtoShipment.MTOServiceItems, "POE"), + PodLocation: Port(mtoShipment.MTOServiceItems, "POD"), } if mtoShipment.Distance != nil { @@ -2737,3 +2740,33 @@ func ReServiceItems(reServiceItems models.ReServiceItems) ghcmessages.ReServiceI } return payload } + +func Port(mtoServiceItems models.MTOServiceItems, portType string) *ghcmessages.Port { + if mtoServiceItems == nil { + return nil + } + + for _, mtoServiceItem := range mtoServiceItems { + var portLocation *models.PortLocation + if portType == "POE" && mtoServiceItem.POELocation != nil { + portLocation = mtoServiceItem.POELocation + } else if portType == "POD" && mtoServiceItem.PODLocation != nil { + portLocation = mtoServiceItem.PODLocation + } + + if portLocation != nil { + return &ghcmessages.Port{ + ID: strfmt.UUID(portLocation.ID.String()), + PortType: portLocation.Port.PortType.String(), + PortCode: portLocation.Port.PortCode, + PortName: portLocation.Port.PortName, + City: portLocation.City.CityName, + County: portLocation.UsPostRegionCity.UsprcCountyNm, + State: portLocation.UsPostRegionCity.UsPostRegion.State.StateName, + Zip: portLocation.UsPostRegionCity.UsprZipID, + Country: portLocation.Country.CountryName, + } + } + } + return nil +} diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go index 439c1216c94..7e201ca5212 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go @@ -906,16 +906,16 @@ func (suite *PayloadsSuite) TestReServiceItems() { marketCodeInternational := models.MarketCodeInternational marketCodeDomestic := models.MarketCodeDomestic poefscReServiceCode := models.ReServiceCodePOEFSC - poedscReServiceCode := models.ReServiceCodePODFSC + podfscReServiceCode := models.ReServiceCodePODFSC poefscServiceName := "International POE Fuel Surcharge" - poedscServiceName := "International POD Fuel Surcharge" + podfscServiceName := "International POD Fuel Surcharge" poefscService := models.ReService{ Code: poefscReServiceCode, Name: poefscServiceName, } podfscService := models.ReService{ - Code: poedscReServiceCode, - Name: poedscServiceName, + Code: podfscReServiceCode, + Name: podfscServiceName, } hhgShipmentType := models.MTOShipmentTypeHHG ubShipmentType := models.MTOShipmentTypeUnaccompaniedBaggage @@ -1219,3 +1219,138 @@ func (suite *PayloadsSuite) TestMTOServiceItemModel() { suite.Equal(handlers.FmtString(models.MarketOconus.FullString()), result.Market, "Expected Market to be OCONUS") }) } + +func (suite *PayloadsSuite) TestPort() { + + suite.Run("returns nil when PortLocation is nil", func() { + var mtoServiceItems models.MTOServiceItems = nil + result := Port(mtoServiceItems, "POE") + suite.Nil(result, "Expected result to be nil when Port Location is nil") + }) + + suite.Run("Success - Maps PortLocation to Port payload", func() { + // Use the factory to create a port location + portLocation := factory.FetchPortLocation(suite.DB(), []factory.Customization{ + { + Model: models.Port{ + PortCode: "PDX", + }, + }, + }, nil) + + mtoServiceItem := factory.BuildMTOServiceItem(nil, []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodePOEFSC, + }, + }, + { + Model: portLocation, + LinkOnly: true, + Type: &factory.PortLocations.PortOfEmbarkation, + }, + }, nil) + + // Actual + mtoServiceItems := models.MTOServiceItems{mtoServiceItem} + result := Port(mtoServiceItems, "POE") + + // Assert + suite.IsType(&ghcmessages.Port{}, result) + suite.Equal(strfmt.UUID(portLocation.ID.String()), result.ID) + suite.Equal(portLocation.Port.PortType.String(), result.PortType) + suite.Equal(portLocation.Port.PortCode, result.PortCode) + suite.Equal(portLocation.Port.PortName, result.PortName) + suite.Equal(portLocation.City.CityName, result.City) + suite.Equal(portLocation.UsPostRegionCity.UsprcCountyNm, result.County) + suite.Equal(portLocation.UsPostRegionCity.UsPostRegion.State.StateName, result.State) + suite.Equal(portLocation.UsPostRegionCity.UsprZipID, result.Zip) + suite.Equal(portLocation.Country.CountryName, result.Country) + }) +} + +func (suite *PayloadsSuite) TestMTOShipment_POE_POD_Locations() { + suite.Run("Only POE Location is set", func() { + // Create mock data for MTOServiceItems with POE and POD + poePortLocation := factory.FetchPortLocation(suite.DB(), []factory.Customization{ + { + Model: models.Port{ + PortCode: "PDX", + }, + }, + }, nil) + + poefscServiceItem := factory.BuildMTOServiceItem(nil, []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodePOEFSC, + Priority: 1, + }, + }, + { + Model: poePortLocation, + LinkOnly: true, + Type: &factory.PortLocations.PortOfEmbarkation, + }, + }, nil) + + mtoShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + MTOServiceItems: models.MTOServiceItems{poefscServiceItem}, + }, + }, + }, nil) + + payload := MTOShipment(nil, &mtoShipment, nil) + + // Assertions + suite.NotNil(payload, "Expected payload to not be nil") + suite.NotNil(payload.PoeLocation, "Expected POELocation to not be nil") + suite.Equal("PDX", payload.PoeLocation.PortCode, "Expected POE Port Code to match") + suite.Equal("PORTLAND INTL", payload.PoeLocation.PortName, "Expected POE Port Name to match") + suite.Nil(payload.PodLocation, "Expected PODLocation to be nil when POELocation is set") + }) + + suite.Run("Only POD Location is set", func() { + // Create mock data for MTOServiceItems with POE and POD + podPortLocation := factory.FetchPortLocation(suite.DB(), []factory.Customization{ + { + Model: models.Port{ + PortCode: "PDX", + }, + }, + }, nil) + + podfscServiceItem := factory.BuildMTOServiceItem(nil, []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodePODFSC, + Priority: 1, + }, + }, + { + Model: podPortLocation, + LinkOnly: true, + Type: &factory.PortLocations.PortOfDebarkation, + }, + }, nil) + + mtoShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + MTOServiceItems: models.MTOServiceItems{podfscServiceItem}, + }, + }, + }, nil) + + payload := MTOShipment(nil, &mtoShipment, nil) + + // Assertions + suite.NotNil(payload, "Expected payload to not be nil") + suite.NotNil(payload.PodLocation, "Expected PODLocation to not be nil") + suite.Equal("PDX", payload.PodLocation.PortCode, "Expected POD Port Code to match") + suite.Equal("PORTLAND INTL", payload.PodLocation.PortName, "Expected POD Port Name to match") + suite.Nil(payload.PoeLocation, "Expected PODLocation to be nil when PODLocation is set") + }) +} diff --git a/pkg/handlers/ghcapi/mto_shipment.go b/pkg/handlers/ghcapi/mto_shipment.go index 0ab30d6ed1a..53011950b49 100644 --- a/pkg/handlers/ghcapi/mto_shipment.go +++ b/pkg/handlers/ghcapi/mto_shipment.go @@ -252,7 +252,7 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment mtoShipment := payloads.MTOShipmentModelFromCreate(payload) - if mtoShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom && mtoShipment.NTSRecordedWeight != nil { + if mtoShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS && mtoShipment.NTSRecordedWeight != nil { previouslyRecordedWeight := *mtoShipment.NTSRecordedWeight mtoShipment.PrimeEstimatedWeight = &previouslyRecordedWeight } @@ -404,7 +404,7 @@ func (h UpdateShipmentHandler) Handle(params mtoshipmentops.UpdateMTOShipmentPar } } - if mtoShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom && mtoShipment.NTSRecordedWeight != nil { + if mtoShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS && mtoShipment.NTSRecordedWeight != nil { previouslyRecordedWeight := *mtoShipment.NTSRecordedWeight mtoShipment.PrimeEstimatedWeight = &previouslyRecordedWeight } diff --git a/pkg/handlers/ghcapi/queues_test.go b/pkg/handlers/ghcapi/queues_test.go index c01f7628f14..4e3c844b5ec 100644 --- a/pkg/handlers/ghcapi/queues_test.go +++ b/pkg/handlers/ghcapi/queues_test.go @@ -181,7 +181,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerMoveInfo() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, }, }, }, nil) diff --git a/pkg/handlers/internalapi/mto_shipment_test.go b/pkg/handlers/internalapi/mto_shipment_test.go index f227a704629..ea0e9a625de 100644 --- a/pkg/handlers/internalapi/mto_shipment_test.go +++ b/pkg/handlers/internalapi/mto_shipment_test.go @@ -490,7 +490,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandlerV1() { params := subtestData.params // Set fields appropriately for NTS-Release - ntsrShipmentType := internalmessages.MTOShipmentTypeHHGOUTOFNTSDOMESTIC + ntsrShipmentType := internalmessages.MTOShipmentTypeHHGOUTOFNTS params.Body.ShipmentType = &ntsrShipmentType params.Body.RequestedPickupDate = strfmt.Date(time.Time{}) params.Body.PickupAddress = nil diff --git a/pkg/handlers/primeapi/move_task_order_test.go b/pkg/handlers/primeapi/move_task_order_test.go index b659b9386d9..735c362f1c8 100644 --- a/pkg/handlers/primeapi/move_task_order_test.go +++ b/pkg/handlers/primeapi/move_task_order_test.go @@ -365,7 +365,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, }, }, @@ -561,7 +561,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { successShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, Status: models.MTOShipmentStatusApproved, }, }, @@ -1680,7 +1680,7 @@ func (suite *HandlerSuite) TestUpdateMTOPostCounselingInfo() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, }, }, diff --git a/pkg/handlers/primeapi/mto_shipment_address.go b/pkg/handlers/primeapi/mto_shipment_address.go index ff90e90ce9e..5f699f384c1 100644 --- a/pkg/handlers/primeapi/mto_shipment_address.go +++ b/pkg/handlers/primeapi/mto_shipment_address.go @@ -50,7 +50,7 @@ func (h UpdateMTOShipmentAddressHandler) Handle(params mtoshipmentops.UpdateMTOS "This shipment is approved, please use the updateShipmentDestinationAddress endpoint to update the destination address of an approved shipment", h.GetTraceIDFromRequest(params.HTTPRequest), nil)), err } - if dbShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom && + if dbShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS && (*dbShipment.PickupAddressID != uuid.Nil && *dbShipment.PickupAddressID == addressID) { return mtoshipmentops.NewUpdateMTOShipmentAddressUnprocessableEntity().WithPayload(payloads.ValidationError( "Cannot update the pickup address of an NTS-Release shipment directly, please update the storage facility address instead", h.GetTraceIDFromRequest(params.HTTPRequest), nil)), err diff --git a/pkg/handlers/primeapiv2/move_task_order_test.go b/pkg/handlers/primeapiv2/move_task_order_test.go index 9636b898359..1f973a2af02 100644 --- a/pkg/handlers/primeapiv2/move_task_order_test.go +++ b/pkg/handlers/primeapiv2/move_task_order_test.go @@ -235,7 +235,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, }, }, @@ -431,7 +431,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { successShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, Status: models.MTOShipmentStatusApproved, }, }, diff --git a/pkg/handlers/primeapiv3/move_task_order_test.go b/pkg/handlers/primeapiv3/move_task_order_test.go index b1585307e7b..8127bbcc233 100644 --- a/pkg/handlers/primeapiv3/move_task_order_test.go +++ b/pkg/handlers/primeapiv3/move_task_order_test.go @@ -233,7 +233,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, }, }, @@ -432,7 +432,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { successShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, Status: models.MTOShipmentStatusApproved, }, }, diff --git a/pkg/models/mto_shipments.go b/pkg/models/mto_shipments.go index 8348185bfca..917e75b7978 100644 --- a/pkg/models/mto_shipments.go +++ b/pkg/models/mto_shipments.go @@ -22,7 +22,7 @@ const ( // NTSRaw is the raw string value of the NTS Shipment Type NTSRaw = "HHG_INTO_NTS" // NTSrRaw is the raw string value of the NTSr Shipment Type - NTSrRaw = "HHG_OUTOF_NTS_DOMESTIC" + NTSrRaw = "HHG_OUTOF_NTS" ) // Market code indicator of international or domestic @@ -38,8 +38,8 @@ const ( MTOShipmentTypeHHG MTOShipmentType = "HHG" // MTOShipmentTypeHHGIntoNTS is an HHG Shipment Type for going into NTS MTOShipmentTypeHHGIntoNTS MTOShipmentType = NTSRaw - // MTOShipmentTypeHHGOutOfNTSDom is an HHG Shipment Type for going out of NTS Domestic - MTOShipmentTypeHHGOutOfNTSDom MTOShipmentType = NTSrRaw + // MTOShipmentTypeHHGOutOfNTS is an HHG Shipment Type for going out of NTS + MTOShipmentTypeHHGOutOfNTS MTOShipmentType = NTSrRaw // MTOShipmentTypeMobileHome is a Shipment Type for MobileHome MTOShipmentTypeMobileHome MTOShipmentType = "MOBILE_HOME" // MTOShipmentTypeBoatHaulAway is a Shipment Type for Boat Haul Away @@ -342,7 +342,7 @@ func DetermineShipmentMarketCode(shipment *MTOShipment) *MTOShipment { shipment.MarketCode = MarketCodeInternational } } - case MTOShipmentTypeHHGOutOfNTSDom: + case MTOShipmentTypeHHGOutOfNTS: if shipment.StorageFacility != nil && shipment.DestinationAddress != nil && shipment.StorageFacility.Address.IsOconus != nil && shipment.DestinationAddress.IsOconus != nil { if isDomestic(&shipment.StorageFacility.Address, shipment.DestinationAddress) { diff --git a/pkg/models/mto_shipments_test.go b/pkg/models/mto_shipments_test.go index 506c69e4d5e..558e80525bd 100644 --- a/pkg/models/mto_shipments_test.go +++ b/pkg/models/mto_shipments_test.go @@ -136,7 +136,7 @@ func (suite *ModelSuite) TestDetermineShipmentMarketCode() { suite.Equal(models.MarketCodeInternational, updatedShipment.MarketCode, "Expected MarketCode to be i") }) - suite.Run("test MTOShipmentTypeHHGOutOfNTSDom with domestic storage and destination", func() { + suite.Run("test MTOShipmentTypeHHGOutOfNTS with domestic storage and destination", func() { storageAddress := models.Address{ IsOconus: models.BoolPointer(false), } @@ -144,7 +144,7 @@ func (suite *ModelSuite) TestDetermineShipmentMarketCode() { IsOconus: models.BoolPointer(false), } shipment := &models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, StorageFacility: &models.StorageFacility{ Address: storageAddress, }, @@ -155,7 +155,7 @@ func (suite *ModelSuite) TestDetermineShipmentMarketCode() { suite.Equal(models.MarketCodeDomestic, updatedShipment.MarketCode, "Expected MarketCode to be d") }) - suite.Run("testMTOShipmentTypeHHGOutOfNTSDom with international destination", func() { + suite.Run("testMTOShipmentTypeHHGOutOfNTS with international destination", func() { storageAddress := models.Address{ IsOconus: models.BoolPointer(false), } @@ -163,7 +163,7 @@ func (suite *ModelSuite) TestDetermineShipmentMarketCode() { IsOconus: models.BoolPointer(true), } shipment := &models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, StorageFacility: &models.StorageFacility{ Address: storageAddress, }, diff --git a/pkg/paperwork/evaluation_report.go b/pkg/paperwork/evaluation_report.go index bf38b056685..b55ca56f9bc 100644 --- a/pkg/paperwork/evaluation_report.go +++ b/pkg/paperwork/evaluation_report.go @@ -251,7 +251,7 @@ func PickShipmentCardLayout(shipmentType models.MTOShipmentType) []TableRow { return PPMShipmentCardLayout case models.MTOShipmentTypeHHGIntoNTS: return NTSShipmentCardLayout - case models.MTOShipmentTypeHHGOutOfNTSDom: + case models.MTOShipmentTypeHHGOutOfNTS: return NTSRShipmentCardLayout case models.MTOShipmentTypeMobileHome: return []TableRow{} @@ -342,7 +342,7 @@ func FormatValuesShipment(shipment models.MTOShipment) ShipmentValues { vals.StorageFacility = *shipment.StorageFacility.Email } - if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS { vals.PickupAddress = formatSingleLineAddress(shipment.StorageFacility.Address) } if shipment.ShipmentType == models.MTOShipmentTypeHHGIntoNTS { diff --git a/pkg/paperwork/evaluation_report_form_creator.go b/pkg/paperwork/evaluation_report_form_creator.go index bb7d879f06a..56a1c0fc12e 100644 --- a/pkg/paperwork/evaluation_report_form_creator.go +++ b/pkg/paperwork/evaluation_report_form_creator.go @@ -353,7 +353,7 @@ func (f *EvaluationReportFormFiller) shipmentCard(shipment models.MTOShipment) e rightX := tableX + labelWidth + valueWidth + gap leftAddressLabel := "" rightAddressLabel := "" - if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS { leftAddressLabel = vals.StorageFacilityName rightAddressLabel = "DELIVERY ADDRESS" } else if shipment.ShipmentType == models.MTOShipmentTypeHHGIntoNTS { @@ -610,7 +610,7 @@ func (f *EvaluationReportFormFiller) formatShipmentType(shipmentType models.MTOS return "PPM" } else if shipmentType == models.MTOShipmentTypeHHGIntoNTS { return "NTS" - } else if shipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + } else if shipmentType == models.MTOShipmentTypeHHGOutOfNTS { return "NTS-R" } else if shipmentType == models.MTOShipmentTypeUnaccompaniedBaggage { return "UB" diff --git a/pkg/paperwork/evaluation_report_test.go b/pkg/paperwork/evaluation_report_test.go index 4b52e778970..a5f19bbedbc 100644 --- a/pkg/paperwork/evaluation_report_test.go +++ b/pkg/paperwork/evaluation_report_test.go @@ -131,6 +131,6 @@ func (suite *PaperworkSuite) TestPickShipmentCardLayout() { suite.ElementsMatch(NTSShipmentCardLayout, PickShipmentCardLayout(models.MTOShipmentTypeHHGIntoNTS)) }) suite.Run("NTS-R", func() { - suite.ElementsMatch(NTSRShipmentCardLayout, PickShipmentCardLayout(models.MTOShipmentTypeHHGOutOfNTSDom)) + suite.ElementsMatch(NTSRShipmentCardLayout, PickShipmentCardLayout(models.MTOShipmentTypeHHGOutOfNTS)) }) } diff --git a/pkg/paperwork/generator.go b/pkg/paperwork/generator.go index 5b478744605..a6b7f431d6a 100644 --- a/pkg/paperwork/generator.go +++ b/pkg/paperwork/generator.go @@ -2,11 +2,15 @@ package paperwork import ( "bytes" + _ "embed" + "fmt" "image" "image/color" "image/jpeg" "image/png" "io" + "log" + "os" "path/filepath" "strings" @@ -92,16 +96,38 @@ func convertTo8BitPNG(in io.Reader, out io.Writer) error { return nil } +// Identifies if a filepath directory is mutable +// This is needed in order to write config and fonts to filesystem +// as the pdfcpu package hard-code requires it at this time +// for initial installation and for form filling +func isDirMutable(path string) bool { + testFile := filepath.Join(path, "tmp") + file, err := os.Create(testFile) + if err != nil { + log.Printf("isDirMutable: failed for %s: %v\n", path, err) + return false + } + file.Close() + os.Remove(testFile) // Cleanup the test file, it is mutable here + return true +} + // NewGenerator creates a new Generator. func NewGenerator(uploader *uploader.Uploader) (*Generator, error) { // Use in memory filesystem for generation. Purpose is to not write // to hard disk due to restrictions in AWS storage. May need better long term solution. afs := storage.NewMemory(storage.NewMemoryParams("", "")).FileSystem() - // Disable ConfiDir for AWS deployment purposes. - // PDFCPU will attempt to create temp dir using os.create(hard disk).This will prevent it. - api.DisableConfigDir() - pdfConfig := model.NewDefaultConfiguration() + tmpDir := os.TempDir() + if !isDirMutable(tmpDir) { + return nil, fmt.Errorf("tmp directory (%s) is not mutable, cannot configure default pdfcpu generator settings", tmpDir) + } + err := api.EnsureDefaultConfigAt(tmpDir) + if err != nil { + return nil, err + } + + pdfConfig := api.LoadConfiguration() // As long as our config was set properly, this will load it and not create a new default config pdfCPU := pdfCPUWrapper{Configuration: pdfConfig} directory, err := afs.TempDir("", "generator") @@ -699,7 +725,7 @@ func (g *Generator) FillPDFForm(jsonData []byte, templateReader io.ReadSeeker, f // Fills form using the template reader with json reader, outputs to byte, to be saved to afero file. formerr := api.FillForm(templateReader, readJSON, buf, conf) if formerr != nil { - return nil, err + return nil, formerr } tempFile, err := g.newTempFileWithName(fileName) // Will use g.newTempFileWithName for proper memory usage, saves the new temp file with the fileName @@ -729,6 +755,10 @@ func (g *Generator) LockPDFForm(templateReader io.ReadSeeker, fileName string) ( buf := new(bytes.Buffer) // Reads all form fields on document as []form.Field fields, err := api.FormFields(templateReader, conf) + if err != nil { + return nil, err + } + // Assembles them to the API's required []string fieldList := make([]string, len(fields)) for i, field := range fields { @@ -786,9 +816,41 @@ func (g *Generator) MergePDFFilesByContents(_ appcontext.AppContext, fileReaders return mergedFile, nil } +// Pdfcpu does not nil check watermarks as of version 0.9.1 +// This map allows us to preemptively nil check before calling the package +func createMapOfOnlyWatermarkedPages(m map[int][]*model.Watermark) map[int][]*model.Watermark { + validMap := make(map[int][]*model.Watermark) + for page, wms := range m { + // Skip entries where the slice is nil or empty + if len(wms) == 0 { + continue + } + + // Filter out nil pointers from the slice + validWms := []*model.Watermark{} + for _, wm := range wms { + if wm != nil { + validWms = append(validWms, wm) + } + } + + // Only add the page to the valid map if the filtered slice is not empty + if len(validWms) > 0 { + validMap[page] = validWms + } + } + return validMap +} + func (g *Generator) AddWatermarks(inputFile afero.File, m map[int][]*model.Watermark) (afero.File, error) { + // Preemptive nil check for the map and its contents + watermarkMap := createMapOfOnlyWatermarkedPages(m) + if watermarkMap[0] == nil { + return nil, fmt.Errorf("no watermarks provided for generation") + } + buf := new(bytes.Buffer) - err := api.AddWatermarksSliceMap(inputFile, buf, m, g.pdfConfig) + err := api.AddWatermarksSliceMap(inputFile, buf, watermarkMap, g.pdfConfig) if err != nil { return nil, err } diff --git a/pkg/paperwork/generator_test.go b/pkg/paperwork/generator_test.go index 235f1b4305c..71d1e0354df 100644 --- a/pkg/paperwork/generator_test.go +++ b/pkg/paperwork/generator_test.go @@ -264,7 +264,7 @@ func (suite *PaperworkSuite) TestCreateMergedPDF() { ctx, err := api.ReadContext(file, generator.pdfConfig) suite.FatalNil(err) - err = validate.XRefTable(ctx.XRefTable) + err = validate.XRefTable(ctx) suite.FatalNil(err) suite.Equal(3, ctx.PageCount) @@ -292,7 +292,7 @@ func (suite *PaperworkSuite) TestCreateMergedPDFByContents() { ctx, err := api.ReadContext(file, generator.pdfConfig) suite.FatalNil(err) - err = validate.XRefTable(ctx.XRefTable) + err = validate.XRefTable(ctx) suite.FatalNil(err) suite.Equal(2, ctx.PageCount) diff --git a/pkg/payment_request/service_param_value_lookups/reference_date_lookup.go b/pkg/payment_request/service_param_value_lookups/reference_date_lookup.go index 6204134060c..05213359768 100644 --- a/pkg/payment_request/service_param_value_lookups/reference_date_lookup.go +++ b/pkg/payment_request/service_param_value_lookups/reference_date_lookup.go @@ -20,7 +20,7 @@ func (r ReferenceDateLookup) lookup(_ appcontext.AppContext, _ *ServiceItemParam // Most shipment types should use RequestedPickupDate, but there are exceptions. switch r.MTOShipment.ShipmentType { - case models.MTOShipmentTypeHHGOutOfNTSDom: + case models.MTOShipmentTypeHHGOutOfNTS: actualPickupDate := r.MTOShipment.ActualPickupDate if actualPickupDate == nil || actualPickupDate.IsZero() { return "", fmt.Errorf("could not find a valid actual pickup date for MTOShipmentID [%s]", r.MTOShipment.ID) diff --git a/pkg/payment_request/service_param_value_lookups/reference_date_lookup_test.go b/pkg/payment_request/service_param_value_lookups/reference_date_lookup_test.go index 50d7c9fb47d..c23a8b48ea8 100644 --- a/pkg/payment_request/service_param_value_lookups/reference_date_lookup_test.go +++ b/pkg/payment_request/service_param_value_lookups/reference_date_lookup_test.go @@ -55,7 +55,7 @@ func (suite *ServiceParamValueLookupsSuite) TestReferenceDateLookup() { }) suite.Run("golden path for NTS-Release", func() { - mtoServiceItem := setupTestData(models.MTOShipmentTypeHHGOutOfNTSDom) + mtoServiceItem := setupTestData(models.MTOShipmentTypeHHGOutOfNTS) paramLookup, err := ServiceParamLookupInitialize(suite.AppContextForTest(), suite.planner, mtoServiceItem, uuid.Must(uuid.NewV4()), mtoServiceItem.MoveTaskOrderID, nil) suite.FatalNoError(err) @@ -83,7 +83,7 @@ func (suite *ServiceParamValueLookupsSuite) TestReferenceDateLookup() { }) suite.Run("error path for NTS-Release", func() { - mtoServiceItem := setupTestData(models.MTOShipmentTypeHHGOutOfNTSDom) + mtoServiceItem := setupTestData(models.MTOShipmentTypeHHGOutOfNTS) // Set the ActualPickupDate to nil mtoServiceItem.MTOShipment.ActualPickupDate = nil diff --git a/pkg/payment_request/service_param_value_lookups/service_param_value_lookups.go b/pkg/payment_request/service_param_value_lookups/service_param_value_lookups.go index 545d95ad5ce..12d76c5ae67 100644 --- a/pkg/payment_request/service_param_value_lookups/service_param_value_lookups.go +++ b/pkg/payment_request/service_param_value_lookups/service_param_value_lookups.go @@ -542,7 +542,7 @@ func getPickupAddressForService(serviceCode models.ReServiceCode, mtoShipment mo var ptrPickupAddress *models.Address var addressType string switch mtoShipment.ShipmentType { - case models.MTOShipmentTypeHHGOutOfNTSDom: + case models.MTOShipmentTypeHHGOutOfNTS: addressType = "storage facility" if mtoShipment.StorageFacility != nil { ptrPickupAddress = &mtoShipment.StorageFacility.Address diff --git a/pkg/payment_request/service_param_value_lookups/service_param_value_lookups_test.go b/pkg/payment_request/service_param_value_lookups/service_param_value_lookups_test.go index fb5c9b78efa..1e56ba76fa3 100644 --- a/pkg/payment_request/service_param_value_lookups/service_param_value_lookups_test.go +++ b/pkg/payment_request/service_param_value_lookups/service_param_value_lookups_test.go @@ -820,7 +820,7 @@ func (suite *ServiceParamValueLookupsSuite) TestServiceParamValueLookup() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, }, }, }, nil) diff --git a/pkg/services/move/move_router.go b/pkg/services/move/move_router.go index 245f3602027..21dbd759282 100644 --- a/pkg/services/move/move_router.go +++ b/pkg/services/move/move_router.go @@ -538,7 +538,7 @@ func (router moveRouter) CompleteServiceCounseling(_ appcontext.AppContext, move // Examine shipments for valid state and how to transition. ppmOnlyMove := true for _, s := range move.MTOShipments { - if s.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom && s.StorageFacilityID == nil { + if s.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS && s.StorageFacilityID == nil { return apperror.NewConflictError(s.ID, "NTS-release shipment must include facility info") } if s.ShipmentType != models.MTOShipmentTypePPM { diff --git a/pkg/services/move/move_router_test.go b/pkg/services/move/move_router_test.go index b0d0eb87aee..ee51c174859 100644 --- a/pkg/services/move/move_router_test.go +++ b/pkg/services/move/move_router_test.go @@ -1268,7 +1268,7 @@ func (suite *MoveServiceSuite) TestCompleteServiceCounseling() { { Model: models.MTOShipment{ ID: uuid.Must(uuid.NewV4()), - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, }, }, { diff --git a/pkg/services/move_task_order/move_task_order_fetcher_test.go b/pkg/services/move_task_order/move_task_order_fetcher_test.go index a80be4aa524..45ad42f0cfc 100644 --- a/pkg/services/move_task_order/move_task_order_fetcher_test.go +++ b/pkg/services/move_task_order/move_task_order_fetcher_test.go @@ -41,7 +41,7 @@ func (suite *MoveTaskOrderServiceSuite) TestMoveTaskOrderFetcher() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, }, }, @@ -735,7 +735,7 @@ func (suite *MoveTaskOrderServiceSuite) TestListAllMoveTaskOrdersFetcher() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, }, }, diff --git a/pkg/services/move_task_order/move_task_order_updater_test.go b/pkg/services/move_task_order/move_task_order_updater_test.go index d38acd2e8ac..778619788a9 100644 --- a/pkg/services/move_task_order/move_task_order_updater_test.go +++ b/pkg/services/move_task_order/move_task_order_updater_test.go @@ -306,7 +306,7 @@ func (suite *MoveTaskOrderServiceSuite) TestMoveTaskOrderUpdater_UpdateStatusSer mtoShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, }, }, { @@ -451,7 +451,7 @@ func (suite *MoveTaskOrderServiceSuite) TestMoveTaskOrderUpdater_UpdatePostCouns }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, }, }, diff --git a/pkg/services/mto_service_item/mto_service_item_creator.go b/pkg/services/mto_service_item/mto_service_item_creator.go index 6bac7e4ec89..6c7da2ec3be 100644 --- a/pkg/services/mto_service_item/mto_service_item_creator.go +++ b/pkg/services/mto_service_item/mto_service_item_creator.go @@ -605,7 +605,7 @@ func (o *mtoServiceItemCreator) CreateMTOServiceItem(appCtx appcontext.AppContex // DLH, DPK, DOP, DDP, DUPK // NTS-release requested pickup dates are for handle out, their pricing is handled differently as their locations are based on storage facilities, not pickup locations - if mtoShipment.PrimeEstimatedWeight != nil && mtoShipment.RequestedPickupDate != nil && mtoShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom { + if mtoShipment.PrimeEstimatedWeight != nil && mtoShipment.RequestedPickupDate != nil && mtoShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS { serviceItemEstimatedPrice, err := o.findEstimatedPrice(appCtx, serviceItem, mtoShipment) if serviceItemEstimatedPrice != 0 && err == nil { serviceItem.PricingEstimate = &serviceItemEstimatedPrice diff --git a/pkg/services/mto_service_item/mto_service_item_updater.go b/pkg/services/mto_service_item/mto_service_item_updater.go index dbdc4405531..b3fee697b47 100644 --- a/pkg/services/mto_service_item/mto_service_item_updater.go +++ b/pkg/services/mto_service_item/mto_service_item_updater.go @@ -419,7 +419,7 @@ func calculateOriginSITRequiredDeliveryDate(appCtx appcontext.AppContext, shipme weight := shipment.PrimeEstimatedWeight - if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS { weight = shipment.NTSRecordedWeight } diff --git a/pkg/services/mto_shipment/mto_shipment_address_updater_test.go b/pkg/services/mto_shipment/mto_shipment_address_updater_test.go index da9621dd279..53f23a5ee4c 100644 --- a/pkg/services/mto_shipment/mto_shipment_address_updater_test.go +++ b/pkg/services/mto_shipment/mto_shipment_address_updater_test.go @@ -44,7 +44,7 @@ func (suite *MTOShipmentServiceSuite) TestUpdateMTOShipmentAddress() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, }, }, @@ -82,7 +82,7 @@ func (suite *MTOShipmentServiceSuite) TestUpdateMTOShipmentAddress() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, Status: models.MTOShipmentStatusApproved, }, @@ -134,7 +134,7 @@ func (suite *MTOShipmentServiceSuite) TestUpdateMTOShipmentAddress() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, Status: models.MTOShipmentStatusApproved, }, diff --git a/pkg/services/mto_shipment/mto_shipment_creator.go b/pkg/services/mto_shipment/mto_shipment_creator.go index 6acb542b68b..c6684b4144f 100644 --- a/pkg/services/mto_shipment/mto_shipment_creator.go +++ b/pkg/services/mto_shipment/mto_shipment_creator.go @@ -80,7 +80,7 @@ func (f mtoShipmentCreator) CreateMTOShipment(appCtx appcontext.AppContext, ship isMobileHomeShipment := shipment.ShipmentType == models.MTOShipmentTypeMobileHome // Check shipment fields that should be there or not based on shipment type. - if shipment.ShipmentType != models.MTOShipmentTypePPM && shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom && !isBoatShipment && !isMobileHomeShipment { + if shipment.ShipmentType != models.MTOShipmentTypePPM && shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS && !isBoatShipment && !isMobileHomeShipment { // No need for a PPM to have a RequestedPickupDate if shipment.RequestedPickupDate == nil || shipment.RequestedPickupDate.IsZero() { return nil, apperror.NewInvalidInputError(uuid.Nil, nil, verrs, @@ -197,7 +197,7 @@ func (f mtoShipmentCreator) CreateMTOShipment(appCtx appcontext.AppContext, ship transactionError := appCtx.NewTransaction(func(txnAppCtx appcontext.AppContext) error { // create pickup and destination addresses - if shipment.PickupAddress != nil && shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom { + if shipment.PickupAddress != nil && shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS { pickupAddress, errAddress := f.addressCreator.CreateAddress(txnAppCtx, shipment.PickupAddress) if errAddress != nil { return apperror.NewInvalidInputError(uuid.Nil, nil, nil, "failed to create pickup address "+errAddress.Error()) @@ -210,7 +210,7 @@ func (f mtoShipmentCreator) CreateMTOShipment(appCtx appcontext.AppContext, ship } shipment.PickupAddress.County = county - } else if shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom && shipment.ShipmentType != models.MTOShipmentTypePPM && !isBoatShipment && !isMobileHomeShipment { + } else if shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS && shipment.ShipmentType != models.MTOShipmentTypePPM && !isBoatShipment && !isMobileHomeShipment { return apperror.NewInvalidInputError(uuid.Nil, nil, nil, "PickupAddress is required to create an HHG, NTS, or UB type MTO shipment") } @@ -304,7 +304,7 @@ func (f mtoShipmentCreator) CreateMTOShipment(appCtx appcontext.AppContext, ship shipment.StorageFacilityID = &shipment.StorageFacility.ID // For NTS-Release set the pick up address to the storage facility - if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS { shipment.PickupAddressID = &shipment.StorageFacility.AddressID shipment.PickupAddress = &shipment.StorageFacility.Address } diff --git a/pkg/services/mto_shipment/mto_shipment_creator_test.go b/pkg/services/mto_shipment/mto_shipment_creator_test.go index 6c31487f0fa..473fb83c3f8 100644 --- a/pkg/services/mto_shipment/mto_shipment_creator_test.go +++ b/pkg/services/mto_shipment/mto_shipment_creator_test.go @@ -97,16 +97,16 @@ func (suite *MTOShipmentServiceSuite) TestCreateMTOShipment() { {nil, models.MTOShipmentTypeHHG, true}, {&time.Time{}, models.MTOShipmentTypeHHG, true}, {models.TimePointer(time.Now()), models.MTOShipmentTypeHHG, false}, - {nil, models.MTOShipmentTypeHHGOutOfNTSDom, false}, - {&time.Time{}, models.MTOShipmentTypeHHGOutOfNTSDom, false}, - {models.TimePointer(time.Now()), models.MTOShipmentTypeHHGOutOfNTSDom, false}, + {nil, models.MTOShipmentTypeHHGOutOfNTS, false}, + {&time.Time{}, models.MTOShipmentTypeHHGOutOfNTS, false}, + {models.TimePointer(time.Now()), models.MTOShipmentTypeHHGOutOfNTS, false}, {nil, models.MTOShipmentTypePPM, false}, {models.TimePointer(time.Now()), models.MTOShipmentTypePPM, false}, } for _, testCase := range testCases { var err error - if testCase.shipmentType == models.MTOShipmentTypeHHGOutOfNTSDom || testCase.shipmentType == models.MTOShipmentTypeHHGIntoNTS { + if testCase.shipmentType == models.MTOShipmentTypeHHGOutOfNTS || testCase.shipmentType == models.MTOShipmentTypeHHGIntoNTS { storageFacility := factory.BuildStorageFacility(nil, nil, nil) storageFacility.ID = uuid.Must(uuid.NewV4()) @@ -378,7 +378,7 @@ func (suite *MTOShipmentServiceSuite) TestCreateMTOShipment() { }{ {models.MTOShipmentTypeHHG, true}, {models.MTOShipmentTypeHHGIntoNTS, false}, - {models.MTOShipmentTypeHHGOutOfNTSDom, false}, + {models.MTOShipmentTypeHHGOutOfNTS, false}, {models.MTOShipmentTypePPM, false}, } @@ -471,7 +471,7 @@ func (suite *MTOShipmentServiceSuite) TestCreateMTOShipment() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, Status: models.MTOShipmentStatusSubmitted, }, }, @@ -502,7 +502,7 @@ func (suite *MTOShipmentServiceSuite) TestCreateMTOShipment() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, Status: models.MTOShipmentStatusSubmitted, NTSRecordedWeight: &ntsRecordedWeight, RequestedDeliveryDate: &requestedDeliveryDate, @@ -796,7 +796,7 @@ func (suite *MTOShipmentServiceSuite) TestCreateMTOShipment() { }{ {"HHG", models.MTOShipmentTypeHHG}, {"HHG_INTO_NTS", models.MTOShipmentTypeHHGIntoNTS}, - {"HHG_OUTOF_NTS_DOMESTIC", models.MTOShipmentTypeHHGOutOfNTSDom}, + {"HHG_OUTOF_NTS", models.MTOShipmentTypeHHGOutOfNTS}, {"MOBILE_HOME", models.MTOShipmentTypeMobileHome}, {"BOAT_HAUL_AWAY", models.MTOShipmentTypeBoatHaulAway}, {"BOAT_TOW_AWAY", models.MTOShipmentTypeBoatTowAway}, @@ -838,7 +838,7 @@ func (suite *MTOShipmentServiceSuite) TestCreateMTOShipment() { }{ {"HHG", models.MTOShipmentTypeHHG}, {"HHG_INTO_NTS", models.MTOShipmentTypeHHGIntoNTS}, - {"HHG_OUTOF_NTS_DOMESTIC", models.MTOShipmentTypeHHGOutOfNTSDom}, + {"HHG_OUTOF_NTS", models.MTOShipmentTypeHHGOutOfNTS}, {"MOBILE_HOME", models.MTOShipmentTypeMobileHome}, {"BOAT_HAUL_AWAY", models.MTOShipmentTypeBoatHaulAway}, {"BOAT_TOW_AWAY", models.MTOShipmentTypeBoatTowAway}, @@ -899,7 +899,7 @@ func (suite *MTOShipmentServiceSuite) TestCreateMTOShipment() { }{ {"HHG", models.MTOShipmentTypeHHG}, {"HHG_INTO_NTS", models.MTOShipmentTypeHHGIntoNTS}, - {"HHG_OUTOF_NTS_DOMESTIC", models.MTOShipmentTypeHHGOutOfNTSDom}, + {"HHG_OUTOF_NTS", models.MTOShipmentTypeHHGOutOfNTS}, {"MOBILE_HOME", models.MTOShipmentTypeMobileHome}, {"BOAT_HAUL_AWAY", models.MTOShipmentTypeBoatHaulAway}, {"BOAT_TOW_AWAY", models.MTOShipmentTypeBoatTowAway}, @@ -980,7 +980,7 @@ func (suite *MTOShipmentServiceSuite) TestCreateMTOShipment() { }{ {"HHG", models.MTOShipmentTypeHHG}, {"HHG_INTO_NTS", models.MTOShipmentTypeHHGIntoNTS}, - {"HHG_OUTOF_NTS_DOMESTIC", models.MTOShipmentTypeHHGOutOfNTSDom}, + {"HHG_OUTOF_NTS", models.MTOShipmentTypeHHGOutOfNTS}, {"MOBILE_HOME", models.MTOShipmentTypeMobileHome}, {"BOAT_HAUL_AWAY", models.MTOShipmentTypeBoatHaulAway}, {"BOAT_TOW_AWAY", models.MTOShipmentTypeBoatTowAway}, @@ -1022,7 +1022,7 @@ func (suite *MTOShipmentServiceSuite) TestCreateMTOShipment() { }{ {"HHG", models.MTOShipmentTypeHHG}, {"HHG_INTO_NTS", models.MTOShipmentTypeHHGIntoNTS}, - {"HHG_OUTOF_NTS_DOMESTIC", models.MTOShipmentTypeHHGOutOfNTSDom}, + {"HHG_OUTOF_NTS", models.MTOShipmentTypeHHGOutOfNTS}, {"MOBILE_HOME", models.MTOShipmentTypeMobileHome}, {"BOAT_HAUL_AWAY", models.MTOShipmentTypeBoatHaulAway}, {"BOAT_TOW_AWAY", models.MTOShipmentTypeBoatTowAway}, @@ -1064,7 +1064,7 @@ func (suite *MTOShipmentServiceSuite) TestCreateMTOShipment() { }{ {"HHG", models.MTOShipmentTypeHHG}, {"HHG_INTO_NTS", models.MTOShipmentTypeHHGIntoNTS}, - {"HHG_OUTOF_NTS_DOMESTIC", models.MTOShipmentTypeHHGOutOfNTSDom}, + {"HHG_OUTOF_NTS", models.MTOShipmentTypeHHGOutOfNTS}, {"MOBILE_HOME", models.MTOShipmentTypeMobileHome}, {"BOAT_HAUL_AWAY", models.MTOShipmentTypeBoatHaulAway}, {"BOAT_TOW_AWAY", models.MTOShipmentTypeBoatTowAway}, diff --git a/pkg/services/mto_shipment/mto_shipment_fetcher.go b/pkg/services/mto_shipment/mto_shipment_fetcher.go index 6796fec5b30..01a19c9fbdb 100644 --- a/pkg/services/mto_shipment/mto_shipment_fetcher.go +++ b/pkg/services/mto_shipment/mto_shipment_fetcher.go @@ -53,6 +53,8 @@ func (f mtoShipmentFetcher) ListMTOShipments(appCtx appcontext.AppContext, moveI "SecondaryDeliveryAddress.Country", "TertiaryDeliveryAddress.Country", "MTOServiceItems.Dimensions", + "MTOServiceItems.PODLocation.Port", + "MTOServiceItems.POELocation.Port", "BoatShipment", "MobileHome", "PPMShipment.W2Address", @@ -146,6 +148,22 @@ func (f mtoShipmentFetcher) ListMTOShipments(appCtx appcontext.AppContext, moveI return nil, err } shipments[i].MTOAgents = agents + + //Pull the port location info back + for _, serviceItem := range shipments[i].MTOServiceItems { + if serviceItem.PODLocation != nil { + loadErr := appCtx.DB().Load(serviceItem.PODLocation, "City", "Country", "UsPostRegionCity.UsPostRegion.State") + if loadErr != nil { + return nil, loadErr + } + } + if serviceItem.POELocation != nil { + loadErr := appCtx.DB().Load(serviceItem.POELocation, "City", "Country", "UsPostRegionCity.UsPostRegion.State") + if loadErr != nil { + return nil, loadErr + } + } + } } return shipments, nil diff --git a/pkg/services/mto_shipment/mto_shipment_fetcher_test.go b/pkg/services/mto_shipment/mto_shipment_fetcher_test.go index 97fc19ca4ea..8a96b1ef739 100644 --- a/pkg/services/mto_shipment/mto_shipment_fetcher_test.go +++ b/pkg/services/mto_shipment/mto_shipment_fetcher_test.go @@ -216,7 +216,7 @@ func (suite *MTOShipmentServiceSuite) TestListMTOShipments() { }, }, []factory.Trait{factory.GetTraitShipmentAddressUpdateRequested}) - serviceItem := testdatagen.MakeMTOServiceItemDomesticCrating(suite.DB(), testdatagen.Assertions{ + serviceItemDCRT := testdatagen.MakeMTOServiceItemDomesticCrating(suite.DB(), testdatagen.Assertions{ ReService: models.ReService{ Code: models.ReServiceCodeDCRT, }, @@ -224,6 +224,31 @@ func (suite *MTOShipmentServiceSuite) TestListMTOShipments() { Move: move, }) + portLocation := factory.FetchPortLocation(suite.DB(), []factory.Customization{ + { + Model: models.Port{ + PortCode: "PDX", + }, + }, + }, nil) + + serviceItemPortFSC := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodePOEFSC, + }, + }, + { + Model: portLocation, + LinkOnly: true, + Type: &factory.PortLocations.PortOfEmbarkation, + }, + { + Model: shipment, + LinkOnly: true, + }, + }, nil) + agents := factory.BuildMTOAgent(suite.DB(), []factory.Customization{ { Model: shipment, @@ -253,7 +278,8 @@ func (suite *MTOShipmentServiceSuite) TestListMTOShipments() { actualShipment := mtoShipments[0] - suite.Equal(serviceItem.ReService.Code, actualShipment.MTOServiceItems[0].ReService.Code) + suite.Equal(serviceItemDCRT.ReService.Code, actualShipment.MTOServiceItems[0].ReService.Code) + suite.Equal(serviceItemPortFSC.ReService.Code, actualShipment.MTOServiceItems[1].ReService.Code) suite.Equal(agents.ID.String(), actualShipment.MTOAgents[0].ID.String()) suite.Equal(shipment.PickupAddress.ID.String(), actualShipment.PickupAddress.ID.String()) suite.Equal(secondaryPickupAddress.ID.String(), actualShipment.SecondaryPickupAddress.ID.String()) diff --git a/pkg/services/mto_shipment/mto_shipment_updater.go b/pkg/services/mto_shipment/mto_shipment_updater.go index 111a05c398e..08a10a19698 100644 --- a/pkg/services/mto_shipment/mto_shipment_updater.go +++ b/pkg/services/mto_shipment/mto_shipment_updater.go @@ -158,7 +158,7 @@ func setNewShipmentFields(appCtx appcontext.AppContext, dbShipment *models.MTOSh dbShipment.NTSRecordedWeight = requestedUpdatedShipment.NTSRecordedWeight } - if requestedUpdatedShipment.PickupAddress != nil && dbShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom { + if requestedUpdatedShipment.PickupAddress != nil && dbShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS { dbShipment.PickupAddress = requestedUpdatedShipment.PickupAddress } @@ -482,7 +482,7 @@ func (f *mtoShipmentUpdater) updateShipmentRecord(appCtx appcontext.AppContext, } - if newShipment.PickupAddress != nil && newShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom { + if newShipment.PickupAddress != nil && newShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS { if dbShipment.PickupAddressID != nil { newShipment.PickupAddress.ID = *dbShipment.PickupAddressID } @@ -687,7 +687,7 @@ func (f *mtoShipmentUpdater) updateShipmentRecord(appCtx appcontext.AppContext, newShipment.StorageFacilityID = &newShipment.StorageFacility.ID // For NTS-Release set the pick up address to the storage facility - if newShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + if newShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS { newShipment.PickupAddressID = &newShipment.StorageFacility.AddressID newShipment.PickupAddress = &newShipment.StorageFacility.Address @@ -727,7 +727,7 @@ func (f *mtoShipmentUpdater) updateShipmentRecord(appCtx appcontext.AppContext, // If the estimated weight was updated on an approved shipment then it would mean the move could qualify for // excess weight risk depending on the weight allowance and other shipment estimated weights - if newShipment.PrimeEstimatedWeight != nil || (newShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom && newShipment.NTSRecordedWeight != nil) { + if newShipment.PrimeEstimatedWeight != nil || (newShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS && newShipment.NTSRecordedWeight != nil) { // checking if the total of shipment weight & new prime estimated weight is 90% or more of allowed weight move, verrs, err := f.moveWeights.CheckExcessWeight(txnAppCtx, dbShipment.MoveTaskOrderID, *newShipment) if verrs != nil && verrs.HasAny() { @@ -739,7 +739,7 @@ func (f *mtoShipmentUpdater) updateShipmentRecord(appCtx appcontext.AppContext, // we only want to update the authorized weight if the shipment is approved and the previous weight is nil // otherwise, shipment_updater will handle updating authorized weight when a shipment is approved - if (dbShipment.PrimeEstimatedWeight == nil || (newShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom && newShipment.NTSRecordedWeight == nil)) && newShipment.Status == models.MTOShipmentStatusApproved { + if (dbShipment.PrimeEstimatedWeight == nil || (newShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS && newShipment.NTSRecordedWeight == nil)) && newShipment.Status == models.MTOShipmentStatusApproved { // updates to prime estimated weight should change the authorized weight of the entitlement // which can be manually adjusted by an office user if needed err = updateAuthorizedWeight(appCtx, newShipment, move) @@ -779,7 +779,7 @@ func (f *mtoShipmentUpdater) updateShipmentRecord(appCtx appcontext.AppContext, } // Check that only NTS Release shipment uses that NTSRecordedWeight field - if newShipment.NTSRecordedWeight != nil && newShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom { + if newShipment.NTSRecordedWeight != nil && newShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS { errMessage := fmt.Sprintf("field NTSRecordedWeight cannot be set for shipment type %s", string(newShipment.ShipmentType)) return apperror.NewInvalidInputError(newShipment.ID, nil, nil, errMessage) } @@ -857,8 +857,8 @@ func (f *mtoShipmentUpdater) updateShipmentRecord(appCtx appcontext.AppContext, // we will compare data here to see if we even need to update the pricing if newShipment.MarketCode == models.MarketCodeInternational && (newShipment.PrimeEstimatedWeight != nil || - newShipment.PickupAddress != nil && newShipment.PickupAddress.PostalCode != dbShipment.PickupAddress.PostalCode || - newShipment.DestinationAddress != nil && newShipment.DestinationAddress.PostalCode != dbShipment.DestinationAddress.PostalCode || + newShipment.PickupAddress != nil && dbShipment.PickupAddress != nil && newShipment.PickupAddress.PostalCode != dbShipment.PickupAddress.PostalCode || + newShipment.DestinationAddress != nil && dbShipment.DestinationAddress != nil && newShipment.DestinationAddress.PostalCode != dbShipment.DestinationAddress.PostalCode || newShipment.RequestedPickupDate != nil && newShipment.RequestedPickupDate.Format("2006-01-02") != dbShipment.RequestedPickupDate.Format("2006-01-02")) { portZip, portType, err := models.GetPortLocationInfoForShipment(appCtx.DB(), newShipment.ID) @@ -1060,9 +1060,9 @@ func (o *mtoShipmentStatusUpdater) setRequiredDeliveryDate(appCtx appcontext.App pickupLocation = shipment.PickupAddress deliveryLocation = &shipment.StorageFacility.Address - case models.MTOShipmentTypeHHGOutOfNTSDom: + case models.MTOShipmentTypeHHGOutOfNTS: if shipment.StorageFacility == nil || shipment.StorageFacility.AddressID == uuid.Nil { - return errors.Errorf("StorageFacility is required for %s shipments", models.MTOShipmentTypeHHGOutOfNTSDom) + return errors.Errorf("StorageFacility is required for %s shipments", models.MTOShipmentTypeHHGOutOfNTS) } err := appCtx.DB().Load(shipment.StorageFacility, "Address", "Address.Country") if err != nil { @@ -1112,9 +1112,9 @@ func reServiceCodesForShipment(shipment models.MTOShipment) []models.ReServiceCo // More info in MB-1140: https://dp3.atlassian.net/browse/MB-1140 // international shipment service items are created in the shipment_approver - switch shipment.ShipmentType { - case models.MTOShipmentTypeHHG: - if shipment.MarketCode != models.MarketCodeInternational { + if shipment.MarketCode != models.MarketCodeInternational { + switch shipment.ShipmentType { + case models.MTOShipmentTypeHHG: originZIP3 := shipment.PickupAddress.PostalCode[0:3] destinationZIP3 := shipment.DestinationAddress.PostalCode[0:3] @@ -1138,51 +1138,51 @@ func reServiceCodesForShipment(shipment models.MTOShipment) []models.ReServiceCo models.ReServiceCodeDPK, models.ReServiceCodeDUPK, } - } - 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, - } - case models.MTOShipmentTypeHHGOutOfNTSDom: - // Need to create: Dom Linehaul, Fuel Surcharge, Dom Origin Price, Dom Destination Price, Dom Unpacking - return []models.ReServiceCode{ - models.ReServiceCodeDLH, - models.ReServiceCodeFSC, - models.ReServiceCodeDOP, - models.ReServiceCodeDDP, - models.ReServiceCodeDUPK, - } - case models.MTOShipmentTypeMobileHome: - // Need to create: Dom Linehaul, Fuel Surcharge, Dom Origin Price, Dom Destination Price, Dom Mobile Home Factor - return []models.ReServiceCode{ - models.ReServiceCodeDLH, - models.ReServiceCodeFSC, - models.ReServiceCodeDOP, - models.ReServiceCodeDDP, - models.ReServiceCodeDMHF, - } - case models.MTOShipmentTypeBoatHaulAway: - // Need to create: Dom Linehaul, Fuel Surcharge, Dom Origin Price, Dom Destination Price, Dom Haul Away Boat Factor - return []models.ReServiceCode{ - models.ReServiceCodeDLH, - models.ReServiceCodeFSC, - models.ReServiceCodeDOP, - models.ReServiceCodeDDP, - models.ReServiceCodeDBHF, - } - case models.MTOShipmentTypeBoatTowAway: - // Need to create: Dom Linehaul, Fuel Surcharge, Dom Origin Price, Dom Destination Price, Dom Tow Away Boat Factor - return []models.ReServiceCode{ - models.ReServiceCodeDLH, - models.ReServiceCodeFSC, - models.ReServiceCodeDOP, - models.ReServiceCodeDDP, - models.ReServiceCodeDBTF, + 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, + } + case models.MTOShipmentTypeHHGOutOfNTS: + // Need to create: Dom Linehaul, Fuel Surcharge, Dom Origin Price, Dom Destination Price, Dom Unpacking + return []models.ReServiceCode{ + models.ReServiceCodeDLH, + models.ReServiceCodeFSC, + models.ReServiceCodeDOP, + models.ReServiceCodeDDP, + models.ReServiceCodeDUPK, + } + case models.MTOShipmentTypeMobileHome: + // Need to create: Dom Linehaul, Fuel Surcharge, Dom Origin Price, Dom Destination Price, Dom Mobile Home Factor + return []models.ReServiceCode{ + models.ReServiceCodeDLH, + models.ReServiceCodeFSC, + models.ReServiceCodeDOP, + models.ReServiceCodeDDP, + models.ReServiceCodeDMHF, + } + case models.MTOShipmentTypeBoatHaulAway: + // Need to create: Dom Linehaul, Fuel Surcharge, Dom Origin Price, Dom Destination Price, Dom Haul Away Boat Factor + return []models.ReServiceCode{ + models.ReServiceCodeDLH, + models.ReServiceCodeFSC, + models.ReServiceCodeDOP, + models.ReServiceCodeDDP, + models.ReServiceCodeDBHF, + } + case models.MTOShipmentTypeBoatTowAway: + // Need to create: Dom Linehaul, Fuel Surcharge, Dom Origin Price, Dom Destination Price, Dom Tow Away Boat Factor + return []models.ReServiceCode{ + models.ReServiceCodeDLH, + models.ReServiceCodeFSC, + models.ReServiceCodeDOP, + models.ReServiceCodeDDP, + models.ReServiceCodeDBTF, + } } } @@ -1392,7 +1392,7 @@ func UpdateDestinationSITServiceItemsSITDeliveryMiles(planner route.Planner, app func updateAuthorizedWeight(appCtx appcontext.AppContext, shipment *models.MTOShipment, move *models.Move) error { var dBAuthorizedWeight int - if shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom { + if shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS { dBAuthorizedWeight = int(*shipment.PrimeEstimatedWeight) } else { dBAuthorizedWeight = int(*shipment.NTSRecordedWeight) @@ -1400,7 +1400,7 @@ func updateAuthorizedWeight(appCtx appcontext.AppContext, shipment *models.MTOSh if len(move.MTOShipments) != 0 { for _, mtoShipment := range move.MTOShipments { if mtoShipment.Status == models.MTOShipmentStatusApproved && mtoShipment.ID != shipment.ID { - if mtoShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom { + if mtoShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS { //uses PrimeEstimatedWeight for HHG and NTS shipments if mtoShipment.PrimeEstimatedWeight != nil { dBAuthorizedWeight += int(*mtoShipment.PrimeEstimatedWeight) diff --git a/pkg/services/mto_shipment/mto_shipment_updater_test.go b/pkg/services/mto_shipment/mto_shipment_updater_test.go index ee3fde58147..63d4af96dc8 100644 --- a/pkg/services/mto_shipment/mto_shipment_updater_test.go +++ b/pkg/services/mto_shipment/mto_shipment_updater_test.go @@ -1336,7 +1336,7 @@ func (suite *MTOShipmentServiceSuite) TestMTOShipmentUpdater() { ntsRecorededWeight := unit.Pound(980) updatedShipment := models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, ID: shipment.ID, NTSRecordedWeight: &ntsRecorededWeight, } @@ -2357,7 +2357,7 @@ func (suite *MTOShipmentServiceSuite) TestUpdateMTOShipmentStatus() { // 2. The shipment must already have the following fields present: // MTOShipmentTypeHHG: ScheduledPickupDate, PrimeEstimatedWeight, PickupAddress, DestinationAddress // MTOShipmentTypeHHGIntoNTS: ScheduledPickupDate, PrimeEstimatedWeight, PickupAddress, StorageFacility - // MTOShipmentTypeHHGOutOfNTSDom: ScheduledPickupDate, NTSRecordedWeight, StorageFacility, DestinationAddress + // MTOShipmentTypeHHGOutOfNTS: ScheduledPickupDate, NTSRecordedWeight, StorageFacility, DestinationAddress // 3. The shipment must not already have a Required Delivery Date // Note that MakeMTOShipment will automatically add a Required Delivery Date if the ScheduledPickupDate // is present, therefore we need to use MakeMTOShipmentMinimal and add the Pickup and Destination addresses @@ -2423,7 +2423,7 @@ func (suite *MTOShipmentServiceSuite) TestUpdateMTOShipmentStatus() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, ScheduledPickupDate: &testdatagen.DateInsidePeakRateCycle, NTSRecordedWeight: &estimatedWeight, Status: models.MTOShipmentStatusSubmitted, @@ -3445,3 +3445,104 @@ 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() { + 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/rules.go b/pkg/services/mto_shipment/rules.go index e9579b24aae..0fe7e481ebc 100644 --- a/pkg/services/mto_shipment/rules.go +++ b/pkg/services/mto_shipment/rules.go @@ -248,7 +248,7 @@ func checkPrimeValidationsOnModel(planner route.Planner) validator { // If it's expired, they can no longer update it. latestEstimatedWeight := older.PrimeEstimatedWeight if newer.PrimeEstimatedWeight != nil { - if newer.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + if newer.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS { verrs.Add("primeEstimatedWeight", "cannot be updated for nts-release shipments, please contact the TOO directly to request updates to this field") } if older.PrimeEstimatedWeight != nil { @@ -270,7 +270,7 @@ func checkPrimeValidationsOnModel(planner route.Planner) validator { } if newer.NTSRecordedWeight != nil { - if newer.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + if newer.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS { verrs.Add("ntsRecordedWeight", "cannot be updated for nts-release shipments, please contact the TOO directly to request updates to this field") } } @@ -296,7 +296,7 @@ func checkPrimeValidationsOnModel(planner route.Planner) validator { } latestPickupAddress = older.PickupAddress latestDestinationAddress = &older.StorageFacility.Address - case models.MTOShipmentTypeHHGOutOfNTSDom: + case models.MTOShipmentTypeHHGOutOfNTS: if older.StorageFacility == nil { // latestPickupAddress is only used for calculating RDD. // We don't want to block an update because we're missing info to calculate RDD @@ -336,10 +336,10 @@ func checkPrimeValidationsOnModel(planner route.Planner) validator { } // If we have all the data, calculate RDD - if latestSchedPickupDate != nil && (latestEstimatedWeight != nil || (older.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom && + if latestSchedPickupDate != nil && (latestEstimatedWeight != nil || (older.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS && older.NTSRecordedWeight != nil)) && latestPickupAddress != nil && latestDestinationAddress != nil { weight := latestEstimatedWeight - if older.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom && older.NTSRecordedWeight != nil { + if older.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS && older.NTSRecordedWeight != nil { weight = older.NTSRecordedWeight } requiredDeliveryDate, err := CalculateRequiredDeliveryDate(appCtx, planner, *latestPickupAddress, diff --git a/pkg/services/mto_shipment/rules_test.go b/pkg/services/mto_shipment/rules_test.go index cc3b8ba8ab1..7f16116bd02 100644 --- a/pkg/services/mto_shipment/rules_test.go +++ b/pkg/services/mto_shipment/rules_test.go @@ -164,7 +164,7 @@ func (suite *MTOShipmentServiceSuite) TestUpdateValidations() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, }, }, @@ -438,7 +438,7 @@ func (suite *MTOShipmentServiceSuite) TestDeleteValidations() { testCases := map[models.MTOShipmentType]bool{ models.MTOShipmentTypeHHG: false, models.MTOShipmentTypeHHGIntoNTS: false, - models.MTOShipmentTypeHHGOutOfNTSDom: false, + models.MTOShipmentTypeHHGOutOfNTS: false, models.MTOShipmentTypeMobileHome: false, models.MTOShipmentTypeBoatHaulAway: false, models.MTOShipmentTypeBoatTowAway: false, diff --git a/pkg/services/mto_shipment/shipment_approver.go b/pkg/services/mto_shipment/shipment_approver.go index 9c4245e9e15..b285684a62b 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" @@ -80,7 +81,8 @@ func (f *shipmentApprover) ApproveShipment(appCtx appcontext.AppContext, shipmen transactionError := appCtx.NewTransaction(func(txnAppCtx appcontext.AppContext) error { // create international shipment service items before approving // we use a database proc to create the basic auto-approved service items - if shipment.ShipmentType == models.MTOShipmentTypeHHG && shipment.MarketCode == models.MarketCodeInternational { + internationalShipmentTypes := []models.MTOShipmentType{models.MTOShipmentTypeHHG, models.MTOShipmentTypeHHGIntoNTS, models.MTOShipmentTypeUnaccompaniedBaggage} + if slices.Contains(internationalShipmentTypes, shipment.ShipmentType) && shipment.MarketCode == models.MarketCodeInternational { err := models.CreateApprovedServiceItemsForShipment(appCtx.DB(), shipment) if err != nil { return err @@ -184,7 +186,7 @@ func (f *shipmentApprover) findShipment(appCtx appcontext.AppContext, shipmentID func (f *shipmentApprover) setRequiredDeliveryDate(appCtx appcontext.AppContext, shipment *models.MTOShipment) error { if shipment.ScheduledPickupDate != nil && shipment.RequiredDeliveryDate == nil && - (shipment.PrimeEstimatedWeight != nil || (shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom && + (shipment.PrimeEstimatedWeight != nil || (shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS && shipment.NTSRecordedWeight != nil)) { var pickupLocation *models.Address @@ -199,9 +201,9 @@ func (f *shipmentApprover) setRequiredDeliveryDate(appCtx appcontext.AppContext, pickupLocation = shipment.PickupAddress deliveryLocation = &shipment.StorageFacility.Address weight = shipment.PrimeEstimatedWeight.Int() - case models.MTOShipmentTypeHHGOutOfNTSDom: + case models.MTOShipmentTypeHHGOutOfNTS: if shipment.StorageFacility == nil { - return errors.Errorf("StorageFacility is required for %s shipments", models.MTOShipmentTypeHHGOutOfNTSDom) + return errors.Errorf("StorageFacility is required for %s shipments", models.MTOShipmentTypeHHGOutOfNTS) } pickupLocation = &shipment.StorageFacility.Address deliveryLocation = shipment.DestinationAddress @@ -256,7 +258,7 @@ func (f *shipmentApprover) updateAuthorizedWeight(appCtx appcontext.AppContext, } var dBAuthorizedWeight int - if shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom { + if shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS { dBAuthorizedWeight = int(*shipment.PrimeEstimatedWeight) } else { dBAuthorizedWeight = int(*shipment.NTSRecordedWeight) @@ -264,7 +266,7 @@ func (f *shipmentApprover) updateAuthorizedWeight(appCtx appcontext.AppContext, if len(move.MTOShipments) != 0 { for _, mtoShipment := range move.MTOShipments { if mtoShipment.Status == models.MTOShipmentStatusApproved && mtoShipment.ID != shipment.ID { - if mtoShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom { + if mtoShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS { //uses PrimeEstimatedWeight for HHG and NTS shipments if mtoShipment.PrimeEstimatedWeight != nil { dBAuthorizedWeight += int(*mtoShipment.PrimeEstimatedWeight) diff --git a/pkg/services/mto_shipment/shipment_approver_test.go b/pkg/services/mto_shipment/shipment_approver_test.go index 40079d020a7..167cffca439 100644 --- a/pkg/services/mto_shipment/shipment_approver_test.go +++ b/pkg/services/mto_shipment/shipment_approver_test.go @@ -14,6 +14,7 @@ import ( "github.com/transcom/mymove/pkg/etag" "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/route" "github.com/transcom/mymove/pkg/route/mocks" "github.com/transcom/mymove/pkg/services" "github.com/transcom/mymove/pkg/services/ghcrateengine" @@ -299,7 +300,7 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() { models.ReServiceCodeIHUPK, } - suite.Equal(4, len(serviceItems)) + suite.Equal(len(expectedReserviceCodes), len(serviceItems)) for i := 0; i < len(serviceItems); i++ { actualReServiceCode := serviceItems[i].ReService.Code suite.True(slices.Contains(expectedReserviceCodes, actualReServiceCode)) @@ -312,6 +313,163 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() { } }) + 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{ + 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("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.BuildNTSShipment(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 @@ -596,7 +754,7 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() { Model: models.MTOShipment{ Status: models.MTOShipmentStatusSubmitted, UsesExternalVendor: true, - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, }, }, }, nil) @@ -652,7 +810,7 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() { // 2. The shipment must already have the following fields present: // MTOShipmentTypeHHG: ScheduledPickupDate, PrimeEstimatedWeight, PickupAddress, DestinationAddress // MTOShipmentTypeHHGIntoNTS: ScheduledPickupDate, PrimeEstimatedWeight, PickupAddress, StorageFacility - // MTOShipmentTypeHHGOutOfNTSDom: ScheduledPickupDate, NTSRecordedWeight, StorageFacility, DestinationAddress + // MTOShipmentTypeHHGOutOfNTS: ScheduledPickupDate, NTSRecordedWeight, StorageFacility, DestinationAddress // 3. The shipment must not already have a Required Delivery Date // Note that MakeMTOShipment will automatically add a Required Delivery Date if the ScheduledPickupDate // is present, therefore we need to use MakeMTOShipmentMinimal and add the Pickup and Destination addresses @@ -718,7 +876,7 @@ func (suite *MTOShipmentServiceSuite) TestApproveShipment() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, ScheduledPickupDate: &testdatagen.DateInsidePeakRateCycle, NTSRecordedWeight: &estimatedWeight, Status: models.MTOShipmentStatusSubmitted, @@ -854,4 +1012,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) + }) } diff --git a/pkg/services/mto_shipment/shipment_billable_weight.go b/pkg/services/mto_shipment/shipment_billable_weight.go index 1b6046ed967..1e39c7a3e4c 100644 --- a/pkg/services/mto_shipment/shipment_billable_weight.go +++ b/pkg/services/mto_shipment/shipment_billable_weight.go @@ -56,13 +56,13 @@ func (f *shipmentBillableWeightCalculator) CalculateShipmentBillableWeight(shipm //Take the lowest between 110% prime estimated and the actual weight, unless shipment is NTSR in which case //it should take lowest between 110% prime estimated weight and ntsRecordedWeight if shipment.PrimeActualWeight != nil && calculatedWeight != nil { - if shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom && shipment.PrimeEstimatedWeight != nil { + if shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS && shipment.PrimeEstimatedWeight != nil { adjustedEstimatedWeight := unit.Pound(shipment.PrimeEstimatedWeight.Float64() * float64(1.1)) if adjustedEstimatedWeight < *calculatedWeight { calculatedWeight = &adjustedEstimatedWeight } } else { - if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom && shipment.NTSRecordedWeight != nil { + if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS && shipment.NTSRecordedWeight != nil { adjustedRecordedWeight := unit.Pound(shipment.NTSRecordedWeight.Float64() * float64(1.1)) if adjustedRecordedWeight < *shipment.PrimeActualWeight { calculatedWeight = &adjustedRecordedWeight diff --git a/pkg/services/mto_shipment/shipment_router_test.go b/pkg/services/mto_shipment/shipment_router_test.go index a7faf3fc37b..68e613f12c9 100644 --- a/pkg/services/mto_shipment/shipment_router_test.go +++ b/pkg/services/mto_shipment/shipment_router_test.go @@ -161,7 +161,7 @@ func (suite *MTOShipmentServiceSuite) TestApprove() { { Model: models.MTOShipment{ UsesExternalVendor: true, - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, Status: models.MTOShipmentStatusSubmitted, }, }, diff --git a/pkg/services/orchestrators/shipment/rules_test.go b/pkg/services/orchestrators/shipment/rules_test.go index 857385e8170..c4485a1eaf4 100644 --- a/pkg/services/orchestrators/shipment/rules_test.go +++ b/pkg/services/orchestrators/shipment/rules_test.go @@ -22,7 +22,7 @@ func (suite *ShipmentSuite) TestCheckShipmentType() { validShipmentTypes := []models.MTOShipmentType{ models.MTOShipmentTypeHHG, models.MTOShipmentTypeHHGIntoNTS, - models.MTOShipmentTypeHHGOutOfNTSDom, + models.MTOShipmentTypeHHGOutOfNTS, models.MTOShipmentTypePPM, } diff --git a/pkg/services/orchestrators/shipment/shipment_creator_test.go b/pkg/services/orchestrators/shipment/shipment_creator_test.go index 30873b04a44..b949db757a2 100644 --- a/pkg/services/orchestrators/shipment/shipment_creator_test.go +++ b/pkg/services/orchestrators/shipment/shipment_creator_test.go @@ -162,7 +162,7 @@ func (suite *ShipmentSuite) TestCreateShipment() { }, "NTS-Release is set to Submitted": { models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, }, models.MTOShipmentStatusSubmitted, }, @@ -195,7 +195,7 @@ func (suite *ShipmentSuite) TestCreateShipment() { ShipmentType: models.MTOShipmentTypeHHGIntoNTS, }, { - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, }, } diff --git a/pkg/services/orchestrators/shipment/shipment_updater_test.go b/pkg/services/orchestrators/shipment/shipment_updater_test.go index 6489871a64e..3e46c05d001 100644 --- a/pkg/services/orchestrators/shipment/shipment_updater_test.go +++ b/pkg/services/orchestrators/shipment/shipment_updater_test.go @@ -201,7 +201,7 @@ func (suite *ShipmentSuite) TestUpdateShipment() { models.MTOShipmentTypeHHG, models.MTOShipmentTypeHHGIntoNTS, - models.MTOShipmentTypeHHGOutOfNTSDom, + models.MTOShipmentTypeHHGOutOfNTS, models.MTOShipmentTypePPM, } diff --git a/pkg/services/order/order_fetcher.go b/pkg/services/order/order_fetcher.go index e11d172ca37..9e1c438883a 100644 --- a/pkg/services/order/order_fetcher.go +++ b/pkg/services/order/order_fetcher.go @@ -614,7 +614,7 @@ func appearedInTOOAtFilter(appearedInTOOAt *time.Time) QueryOption { func requestedMoveDateFilter(requestedMoveDate *string) QueryOption { return func(query *pop.Query) { if requestedMoveDate != nil { - query.Where("(mto_shipments.requested_pickup_date = ? OR ppm_shipments.expected_departure_date = ? OR (mto_shipments.shipment_type = 'HHG_OUTOF_NTS_DOMESTIC' AND mto_shipments.requested_delivery_date = ?))", *requestedMoveDate, *requestedMoveDate, *requestedMoveDate) + query.Where("(mto_shipments.requested_pickup_date = ? OR ppm_shipments.expected_departure_date = ? OR (mto_shipments.shipment_type = 'HHG_OUTOF_NTS' AND mto_shipments.requested_delivery_date = ?))", *requestedMoveDate, *requestedMoveDate, *requestedMoveDate) } } } @@ -692,7 +692,7 @@ func gblocFilterForTOO(gbloc *string) QueryOption { if gbloc != nil { // Note: extra parens necessary to keep precedence correct when AND'ing all filters together. query.Where("((mto_shipments.shipment_type != ? AND move_to_gbloc.gbloc = ?) OR (mto_shipments.shipment_type = ? AND orders.gbloc = ?))", - models.MTOShipmentTypeHHGOutOfNTSDom, *gbloc, models.MTOShipmentTypeHHGOutOfNTSDom, *gbloc) + models.MTOShipmentTypeHHGOutOfNTS, *gbloc, models.MTOShipmentTypeHHGOutOfNTS, *gbloc) } } } diff --git a/pkg/services/order/order_fetcher_test.go b/pkg/services/order/order_fetcher_test.go index aa9e11fed89..06073d30a34 100644 --- a/pkg/services/order/order_fetcher_test.go +++ b/pkg/services/order/order_fetcher_test.go @@ -1683,7 +1683,7 @@ func (suite *OrderServiceSuite) TestListOrdersForTOOWithNTSRelease() { factory.BuildMoveWithShipment(suite.DB(), []factory.Customization{ { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, }, }, }, nil) diff --git a/pkg/services/order/order_updater.go b/pkg/services/order/order_updater.go index 38c991a112a..02b8c848ca5 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/pkg/services/payment_request/payment_request_creator_test.go b/pkg/services/payment_request/payment_request_creator_test.go index cc8d50d01d2..3831f354b47 100644 --- a/pkg/services/payment_request/payment_request_creator_test.go +++ b/pkg/services/payment_request/payment_request_creator_test.go @@ -1423,7 +1423,7 @@ func (suite *PaymentRequestServiceSuite) TestCreatePaymentRequestCheckOnNTSRelea }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, PrimeActualWeight: &testOriginalWeight, ActualPickupDate: &actualPickupDate, }, diff --git a/pkg/services/ppmshipment/payment_packet_creator.go b/pkg/services/ppmshipment/payment_packet_creator.go index b8691d104e7..ab949487f60 100644 --- a/pkg/services/ppmshipment/payment_packet_creator.go +++ b/pkg/services/ppmshipment/payment_packet_creator.go @@ -3,12 +3,9 @@ package ppmshipment import ( "fmt" "io" - "time" "github.com/gofrs/uuid" "github.com/pdfcpu/pdfcpu/pkg/pdfcpu" - "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model" - "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types" "github.com/pkg/errors" "go.uber.org/zap" @@ -142,26 +139,9 @@ func (p *paymentPacketCreator) Generate(appCtx appcontext.AppContext, ppmShipmen return nil, fmt.Errorf("%s: %w", errMsgPrefix, err) } - watermarks, err := buildWaterMarks(bookmarks, p.pdfGenerator) - if err != nil { - errMsgPrefix = fmt.Sprintf("%s: %s", errMsgPrefix, "failed to generate watermarks for PDF") - appCtx.Logger().Error(errMsgPrefix, zap.Error(err)) - return nil, fmt.Errorf("%s: %w", errMsgPrefix, err) - } - - // Apply bookmarks and watermarks based on flag - if addWatermarks && len(watermarks) > 0 { - pdfWithWatermarks, err := p.pdfGenerator.AddWatermarks(finalMergePdf, watermarks) - if err != nil { - errMsgPrefix = fmt.Sprintf("%s: %s", errMsgPrefix, "failed to add watermarks to PDF") - appCtx.Logger().Error(errMsgPrefix, zap.Error(err)) - return nil, fmt.Errorf("%s: %w", errMsgPrefix, err) - } - if addBookmarks { - return p.pdfGenerator.AddPdfBookmarks(pdfWithWatermarks, bookmarks) - } - return pdfWithWatermarks, nil - } + // It was discovered during implementation of B-21938 that watermarks were not functional. + // This is because the watermark func was using bookmarks, not watermarks. + // See https://github.com/transcom/mymove/pull/14496 for removal if addBookmarks { return p.pdfGenerator.AddPdfBookmarks(finalMergePdf, bookmarks) @@ -210,51 +190,6 @@ func buildBookMarks(fileNamesToMerge []string, sortedPaymentPacketItems map[int] return bookmarks, nil } -// generate watermarks which will serve as page footer labels -func buildWaterMarks(bookMarks []pdfcpu.Bookmark, pdfGenerator paperwork.Generator) (map[int][]*model.Watermark, error) { - m := make(map[int][]*model.Watermark) - - opacity := 1.0 - onTop := true - update := false - unit := types.POINTS - - desc := fmt.Sprintf("font:Times-Italic, points:10, sc:1 abs, pos:bc, off:0 8, rot:0, op:%f", opacity) - - creationTimeStamp := time.Now().UTC().Format("2006-01-02T15:04:05.000Z") - totalPages := bookMarks[len(bookMarks)-1].PageThru - currentPage := 1 - bookMarkIndex := 0 - for _, bm := range bookMarks { - cnt := bm.PageThru - bm.PageFrom - for j := 0; j <= cnt; j++ { - // do not add watermark on SSW pages - if currentPage < 4 { - currentPage++ - continue - } - wmText := bm.Title - // we really can't use the bookmark title for the SSW+Orders. - // we will just label it as only Orders - if currentPage > 3 && bookMarkIndex == 0 { - wmText = "Orders" - } - wms := make([]*model.Watermark, 0) - pagingInfo := fmt.Sprintf("Page %d of %d", currentPage, totalPages) - text := fmt.Sprintf("%s - Payment Packet[%s] (Creation Date: %v)", pagingInfo, wmText, creationTimeStamp) - - wm, _ := pdfGenerator.CreateTextWatermark(text, desc, onTop, update, unit) - wms = append(wms, wm) - // note: use current page because map is 1 based - m[currentPage] = wms - currentPage++ - } - bookMarkIndex++ - } - - return m, nil -} - func buildPaymentPacketItemsMap(ppmShipment *models.PPMShipment) map[int]paymentPacketItem { // items are sorted based on key(int), key represents order index sortedPaymentPacketItems := make(map[int]paymentPacketItem) diff --git a/pkg/services/reweigh/rules_test.go b/pkg/services/reweigh/rules_test.go index 79b6ef569a2..1ec6912e41f 100644 --- a/pkg/services/reweigh/rules_test.go +++ b/pkg/services/reweigh/rules_test.go @@ -199,7 +199,7 @@ func (suite *ReweighSuite) TestValidationRules() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, }, }, 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..7ae14dbbeda 100644 --- a/pkg/services/shipment_address_update/shipment_address_update_requester.go +++ b/pkg/services/shipment_address_update/shipment_address_update_requester.go @@ -245,7 +245,7 @@ func (f *shipmentAddressUpdateRequester) RequestShipmentDeliveryAddressUpdate(ap if shipment.MoveTaskOrder.AvailableToPrimeAt == nil { return nil, apperror.NewUnprocessableEntityError("destination address update requests can only be created for moves that are available to the Prime") } - if shipment.ShipmentType != models.MTOShipmentTypeHHG && shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom { + if shipment.ShipmentType != models.MTOShipmentTypeHHG && shipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS { return nil, apperror.NewUnprocessableEntityError("destination address update requests can only be created for HHG and NTS-Release shipments") } if eTag != etag.GenerateEtag(shipment.UpdatedAt) { @@ -344,7 +344,7 @@ func (f *shipmentAddressUpdateRequester) RequestShipmentDeliveryAddressUpdate(ap if err != nil { return nil, err } - } else if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + } else if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS { updateNeedsTOOReview, err = f.doesDeliveryAddressUpdateChangeShipmentPricingType(shipment.StorageFacility.Address, addressUpdate.OriginalAddress, newAddress) if err != nil { return nil, err @@ -360,7 +360,7 @@ func (f *shipmentAddressUpdateRequester) RequestShipmentDeliveryAddressUpdate(ap if err != nil { return nil, err } - } else if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + } else if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS { updateNeedsTOOReview, err = f.doesDeliveryAddressUpdateChangeMileageBracket(appCtx, shipment.StorageFacility.Address, addressUpdate.OriginalAddress, newAddress) if err != nil { return nil, err @@ -479,7 +479,7 @@ func (f *shipmentAddressUpdateRequester) ReviewShipmentAddressChange(appCtx appc if err != nil { return nil, err } - } else if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + } else if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS { haulPricingTypeHasChanged, err = f.doesDeliveryAddressUpdateChangeShipmentPricingType(shipment.StorageFacility.Address, addressUpdate.OriginalAddress, addressUpdate.NewAddress) if err != nil { return nil, err @@ -547,7 +547,7 @@ func (f *shipmentAddressUpdateRequester) ReviewShipmentAddressChange(appCtx appc // handling NTS shipments that don't have a pickup address var pickupAddress models.Address - if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS { pickupAddress = shipment.StorageFacility.Address } else { pickupAddress = *shipment.PickupAddress diff --git a/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet.go b/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet.go index a0d4ab3a421..63722c99422 100644 --- a/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet.go +++ b/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet.go @@ -208,7 +208,7 @@ func SSWGetEntitlement(grade internalmessages.OrderPayGrade, hasDependents bool, } // Calculates cost for the Remaining PPM Incentive (pre-tax) field on page 2 of SSW form. -func CalculateRemainingPPMEntitlement(finalIncentive *unit.Cents, sitMemberPaid float64, sitGTCCPaid float64, aoa *unit.Cents) float64 { +func CalculateRemainingPPMEntitlement(finalIncentive *unit.Cents, aoa *unit.Cents) float64 { // FinalIncentive var finalIncentiveFloat float64 = 0 if finalIncentive != nil { @@ -223,7 +223,7 @@ func CalculateRemainingPPMEntitlement(finalIncentive *unit.Cents, sitMemberPaid // This costing is computed by taking the Actual Obligations 100% GCC plus the // SIT cost calculated (if SIT was approved and accepted) minus any Advance // Operating Allowance (AOA) the customer identified as receiving in the Document upload process - return (finalIncentiveFloat + sitMemberPaid + sitGTCCPaid) - aoaFloat + return finalIncentiveFloat - aoaFloat } const ( @@ -328,7 +328,7 @@ func (s *SSWPPMComputer) FormatValuesShipmentSummaryWorksheetFormPage2(data mode if data.IsActualExpenseReimbursement { data.PPMRemainingEntitlement = 0.0 } else { - data.PPMRemainingEntitlement = CalculateRemainingPPMEntitlement(data.PPMShipment.FinalIncentive, expensesMap["StorageMemberPaid"], expensesMap["StorageGTCCPaid"], data.PPMShipment.AdvanceAmountReceived) + data.PPMRemainingEntitlement = CalculateRemainingPPMEntitlement(data.PPMShipment.FinalIncentive, data.PPMShipment.AdvanceAmountReceived) } page2.PPMRemainingEntitlement = FormatDollars(data.PPMRemainingEntitlement) @@ -336,7 +336,15 @@ func (s *SSWPPMComputer) FormatValuesShipmentSummaryWorksheetFormPage2(data mode if err != nil { return page2, err } - page2.Disbursement = formatDisbursement(expensesMap, data.PPMRemainingEntitlement) + var finalIncentiveFloat float64 = 0 + if data.PPMShipment.FinalIncentive != nil { + finalIncentiveFloat = float64(*data.PPMShipment.FinalIncentive) / 100 + } + var aoaFloat float64 = 0 + if data.PPMShipment.AdvanceAmountReceived != nil { + aoaFloat = float64(*data.PPMShipment.AdvanceAmountReceived) / 100 + } + page2.Disbursement = formatDisbursement(expensesMap, finalIncentiveFloat-aoaFloat) } else { page2.PreparationDate2 = formatAOADate(data.SignedCertifications, data.PPMShipment.ID) page2.Disbursement = "N/A" @@ -458,7 +466,7 @@ func formatAdditionalShipments(ssfd models.ShipmentSummaryFormData) (map[string] } // PPM Status instead of shipment status page3Map[fmt.Sprintf("AddShipmentStatus%d", i)] = FormatCurrentPPMStatus(*shipment.PPMShipment) - case shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom: + case shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS: page3Map[fmt.Sprintf("AddShipmentNumberAndTypes%d", i)] = *shipment.ShipmentLocator + " NTS Release" case shipment.ShipmentType == models.MTOShipmentTypeHHGIntoNTS: page3Map[fmt.Sprintf("AddShipmentNumberAndTypes%d", i)] = *shipment.ShipmentLocator + " NTS" @@ -950,7 +958,7 @@ func formatDisbursement(expensesMap map[string]float64, ppmRemainingEntitlement disbursementGTCC = 0 } else { // Disbursement Member is remaining entitlement plus member SIT minus GTCC Disbursement, not less than 0. - disbursementMember = ppmRemainingEntitlement + expensesMap["StorageMemberPaid"] - disbursementGTCC + disbursementMember = ppmRemainingEntitlement + expensesMap["StorageMemberPaid"] } // Return formatted values in string diff --git a/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_test.go b/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_test.go index 8f89327f6f2..b94768aaf41 100644 --- a/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_test.go +++ b/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_test.go @@ -26,6 +26,11 @@ import ( "github.com/transcom/mymove/pkg/uploader" ) +// Helper function to format disbursement field for equal checks +var expectedDisbursementString = func(expectedGTCC int, expectedMember int) string { + return "GTCC: " + FormatDollars((models.CentPointer(unit.Cents(expectedGTCC)).ToMillicents().ToDollarFloat())) + "\nMember: " + FormatDollars(models.CentPointer(unit.Cents(expectedMember)).ToMillicents().ToDollarFloat()) +} + func (suite *ShipmentSummaryWorksheetServiceSuite) TestFetchDataShipmentSummaryWorksheet() { //advanceID, _ := uuid.NewV4() ordersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION @@ -693,7 +698,8 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestMemberPaidRemainingPPMEnt sswPPMComputer := NewSSWPPMComputer(mockPPMCloseoutFetcher) expensesMap := SubTotalExpenses(ssd.MovingExpenses) sswPage2, _ := sswPPMComputer.FormatValuesShipmentSummaryWorksheetFormPage2(ssd, true, expensesMap) - suite.Equal("$4.00", sswPage2.PPMRemainingEntitlement) + suite.Equal("$3.00", sswPage2.PPMRemainingEntitlement) + suite.Equal(expectedDisbursementString(0, 400), sswPage2.Disbursement) } func (suite *ShipmentSummaryWorksheetServiceSuite) TestAOAPacketPPMEntitlementFormatValuesShipmentSummaryWorksheetFormPage2() { @@ -777,7 +783,8 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestNullCheckForFinalIncentiv sswPPMComputer := NewSSWPPMComputer(mockPPMCloseoutFetcher) expensesMap := SubTotalExpenses(ssd.MovingExpenses) sswPage2, _ := sswPPMComputer.FormatValuesShipmentSummaryWorksheetFormPage2(ssd, true, expensesMap) - suite.Equal("$1.00", sswPage2.PPMRemainingEntitlement) + suite.Equal("$0.00", sswPage2.PPMRemainingEntitlement) + suite.Equal(expectedDisbursementString(0, 100), sswPage2.Disbursement) } func (suite *ShipmentSummaryWorksheetServiceSuite) TestGTCCPaidRemainingPPMEntitlementFormatValuesShipmentSummaryWorksheetFormPage2() { @@ -827,7 +834,8 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestGTCCPaidRemainingPPMEntit mockPPMCloseoutFetcher := &mocks.PPMCloseoutFetcher{} sswPPMComputer := NewSSWPPMComputer(mockPPMCloseoutFetcher) sswPage2, _ := sswPPMComputer.FormatValuesShipmentSummaryWorksheetFormPage2(ssd, true, expensesMap) - suite.Equal("$105.00", sswPage2.PPMRemainingEntitlement) + suite.Equal("$5.00", sswPage2.PPMRemainingEntitlement) + suite.Equal(expectedDisbursementString(500, 500), sswPage2.Disbursement) } func (suite *ShipmentSummaryWorksheetServiceSuite) TestGroupExpenses() { paidWithGTCC := false @@ -1320,11 +1328,6 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestFillSSWPDFForm() { func (suite *ShipmentSummaryWorksheetServiceSuite) TestActualExpenseReimbursementCalculations() { - // Helper function to format disbursement field for equal checks - expectedDisbursementString := func(expectedGTCC int, expectedMember int) string { - return "GTCC: " + FormatDollars((models.CentPointer(unit.Cents(expectedGTCC)).ToMillicents().ToDollarFloat())) + "\nMember: " + FormatDollars(models.CentPointer(unit.Cents(expectedMember)).ToMillicents().ToDollarFloat()) - } - fakeS3 := storageTest.NewFakeS3Storage(true) userUploader, uploaderErr := uploader.NewUserUploader(fakeS3, 25*uploader.MB) suite.FatalNoError(uploaderErr) @@ -1707,7 +1710,7 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestFormatAdditionalShipments }, { PPMShipment: &ppm2, - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, ShipmentLocator: &locator, RequestedPickupDate: &now, Status: models.MTOShipmentStatusSubmitted, @@ -1809,7 +1812,7 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestFormatAdditionalShipments if index == 0 { if shipment.ShipmentType == models.MTOShipmentTypePPM { suite.Equal(fmt.Sprintf("%s %s", locator, string(shipment.ShipmentType)), value) - } else if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom { + } else if shipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS { suite.Equal(fmt.Sprintf("%s %s", locator, "NTS Release"), value) } else if shipment.ShipmentType == models.MTOShipmentTypeHHGIntoNTS { suite.Equal(fmt.Sprintf("%s %s", locator, "NTS"), value) @@ -1950,23 +1953,23 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestFormatDisbursement() { // Test case 1: GTCC calculation B is less than GTCC calculation A // Additionally, Member should not be less than 0 - expectedResult := "GTCC: " + FormatDollars(100.00) + "\nMember: " + FormatDollars(0) + expectedResult := "GTCC: " + FormatDollars(100.00) + "\nMember: " + FormatDollars(100.00) expensesMap["TotalGTCCPaid"] = 200.00 expensesMap["StorageGTCCPaid"] = 300.00 ppmRemainingEntitlement := 60.00 expensesMap["StorageMemberPaid"] = 40.00 result := formatDisbursement(expensesMap, ppmRemainingEntitlement) - suite.Equal(result, expectedResult) + suite.Equal(expectedResult, result) // Test case 2: GTCC calculation A is less than GTCC calculation B - expectedResult = "GTCC: " + FormatDollars(100.00) + "\nMember: " + FormatDollars(400.00) + expectedResult = "GTCC: " + FormatDollars(100.00) + "\nMember: " + FormatDollars(500.00) expensesMap = make(map[string]float64) expensesMap["TotalGTCCPaid"] = 60.00 expensesMap["StorageGTCCPaid"] = 40.00 ppmRemainingEntitlement = 300.00 expensesMap["StorageMemberPaid"] = 200.00 result = formatDisbursement(expensesMap, ppmRemainingEntitlement) - suite.Equal(result, expectedResult) + suite.Equal(expectedResult, result) // Test case 3: GTCC calculation is less than 0 expectedResult = "GTCC: " + FormatDollars(0) + "\nMember: " + FormatDollars(-250.00) @@ -1976,5 +1979,5 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestFormatDisbursement() { ppmRemainingEntitlement = -300.00 expensesMap["StorageMemberPaid"] = 50.00 result = formatDisbursement(expensesMap, ppmRemainingEntitlement) - suite.Equal(result, expectedResult) + suite.Equal(expectedResult, result) } diff --git a/pkg/services/sit_extension/sit_extension_creator_test.go b/pkg/services/sit_extension/sit_extension_creator_test.go index 9a3a15afc98..4d7a9e310ef 100644 --- a/pkg/services/sit_extension/sit_extension_creator_test.go +++ b/pkg/services/sit_extension/sit_extension_creator_test.go @@ -93,7 +93,7 @@ func (suite *SitExtensionServiceSuite) TestSITExtensionCreator() { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, UsesExternalVendor: true, }, }, diff --git a/pkg/testdatagen/make_mto_shipment.go b/pkg/testdatagen/make_mto_shipment.go index 8c4fee37184..9bfb8127d15 100644 --- a/pkg/testdatagen/make_mto_shipment.go +++ b/pkg/testdatagen/make_mto_shipment.go @@ -13,10 +13,10 @@ import ( // makeMTOShipment creates a single MTOShipment and associated set relationships // It will make a move record, if one is not provided. -// It will make pickup addresses if the shipment type is not one of (HHGOutOfNTSDom, PPM) -// It will make delivery addresses if the shipment type is not one of (HHGOutOfNTSDom, PPM) +// It will make pickup addresses if the shipment type is not one of (HHGOutOfNTS, PPM) +// It will make delivery addresses if the shipment type is not one of (HHGOutOfNTS, PPM) // It will make a storage facility if the shipment type is -// HHGOutOfNTSDom +// HHGOutOfNTS // // Deprecated: use factory.BuildMTOShipment func makeMTOShipment(db *pop.Connection, assertions Assertions) models.MTOShipment { @@ -38,7 +38,7 @@ func makeMTOShipment(db *pop.Connection, assertions Assertions) models.MTOShipme shipmentStatus = mtoShipment.Status } - shipmentHasPickupDetails := mtoShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTSDom && mtoShipment.ShipmentType != models.MTOShipmentTypePPM + shipmentHasPickupDetails := mtoShipment.ShipmentType != models.MTOShipmentTypeHHGOutOfNTS && mtoShipment.ShipmentType != models.MTOShipmentTypePPM shipmentHasDeliveryDetails := mtoShipment.ShipmentType != models.MTOShipmentTypeHHGIntoNTS && mtoShipment.ShipmentType != models.MTOShipmentTypePPM var pickupAddress, secondaryPickupAddress models.Address @@ -95,7 +95,7 @@ func makeMTOShipment(db *pop.Connection, assertions Assertions) models.MTOShipme var storageFacilityID *uuid.UUID var storageFacility models.StorageFacility - if mtoShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTSDom || + if mtoShipment.ShipmentType == models.MTOShipmentTypeHHGOutOfNTS || mtoShipment.ShipmentType == models.MTOShipmentTypeHHGIntoNTS { if mtoShipment.StorageFacility != nil { if isZeroUUID(mtoShipment.StorageFacility.ID) { diff --git a/pkg/testdatagen/scenario/shared.go b/pkg/testdatagen/scenario/shared.go index 9d83ef2f47e..451f444ade5 100644 --- a/pkg/testdatagen/scenario/shared.go +++ b/pkg/testdatagen/scenario/shared.go @@ -4116,7 +4116,7 @@ func createNTSRMove(appCtx appcontext.AppContext) { }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, }, }, }, nil) @@ -6520,7 +6520,7 @@ func createMoveWithHHGAndNTSRPaymentRequest(appCtx appcontext.AppContext, userUp ID: uuid.Must(uuid.NewV4()), PrimeEstimatedWeight: &estimatedWeight, PrimeActualWeight: &actualWeight, - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, ApprovedDate: models.TimePointer(time.Now()), ActualPickupDate: models.TimePointer(time.Now()), Status: models.MTOShipmentStatusApproved, @@ -7202,7 +7202,7 @@ func createMoveWithHHGAndNTSRMissingInfo(appCtx appcontext.AppContext, moveRoute Model: models.MTOShipment{ ID: uuid.Must(uuid.NewV4()), PrimeEstimatedWeight: &estimatedWeight, - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, ApprovedDate: models.TimePointer(time.Now()), Status: models.MTOShipmentStatusSubmitted, }, @@ -7630,7 +7630,7 @@ func createMoveWith2ShipmentsAndPaymentRequest(appCtx appcontext.AppContext, use ID: uuid.Must(uuid.NewV4()), PrimeEstimatedWeight: &estimatedWeight, PrimeActualWeight: &actualWeight, - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, ApprovedDate: models.TimePointer(time.Now()), Status: models.MTOShipmentStatusApproved, }, @@ -10902,7 +10902,7 @@ func CreateNeedsServicesCounselingMinimalNTSR(appCtx appcontext.AppContext, orde }, { Model: models.MTOShipment{ - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, RequestedDeliveryDate: &requestedDeliveryDate, }, }, diff --git a/pkg/testdatagen/scenario/subscenarios.go b/pkg/testdatagen/scenario/subscenarios.go index e40a6f5a602..5a5efe5a2c1 100644 --- a/pkg/testdatagen/scenario/subscenarios.go +++ b/pkg/testdatagen/scenario/subscenarios.go @@ -204,7 +204,7 @@ func subScenarioHHGServicesCounseling(appCtx appcontext.AppContext, userUploader //Shipment Types -- HHG, NTS, NTSR hhg := models.MTOShipmentTypeHHG nts := models.MTOShipmentTypeHHGIntoNTS - ntsR := models.MTOShipmentTypeHHGOutOfNTSDom + ntsR := models.MTOShipmentTypeHHGOutOfNTS //Destination Types -- PLEAD, HOR, HOS, OTHER plead := models.DestinationTypePlaceEnteredActiveDuty @@ -594,7 +594,7 @@ func subScenarioTXOQueues(appCtx appcontext.AppContext, userUploader *uploader.U //shipment type hhg := models.MTOShipmentTypeHHG nts := models.MTOShipmentTypeHHGIntoNTS - ntsR := models.MTOShipmentTypeHHGOutOfNTSDom + ntsR := models.MTOShipmentTypeHHGOutOfNTS //orders type retirement := internalmessages.OrdersTypeRETIREMENT diff --git a/pkg/testdatagen/testharness/make_move.go b/pkg/testdatagen/testharness/make_move.go index aae7c83ecfa..fd19ae1f917 100644 --- a/pkg/testdatagen/testharness/make_move.go +++ b/pkg/testdatagen/testharness/make_move.go @@ -7352,7 +7352,7 @@ func MakeNTSRMoveWithAddressChangeRequest(appCtx appcontext.AppContext) models.S { Model: models.MTOShipment{ Status: models.MTOShipmentStatusApproved, - ShipmentType: models.MTOShipmentTypeHHGOutOfNTSDom, + ShipmentType: models.MTOShipmentTypeHHGOutOfNTS, NTSRecordedWeight: &NTSRecordedWeight, ServiceOrderNumber: &serviceOrderNumber, RequestedPickupDate: &requestedPickupDate, diff --git a/playwright/tests/admin/officeUsers.spec.js b/playwright/tests/admin/officeUsers.spec.js index fd7646a2604..64fb83f27dd 100644 --- a/playwright/tests/admin/officeUsers.spec.js +++ b/playwright/tests/admin/officeUsers.spec.js @@ -88,10 +88,10 @@ test.describe('Office User Create Page', () => { // The autocomplete form results in multiple matching elements, so // pick the input element - await page.getByLabel('Transportation Office').nth(0).fill('JPPSO Testy McTest'); + await page.getByLabel('Transportation Office').nth(0).fill('PPPO Scott AFB - USAF'); // the autocomplete might return multiples because of concurrent // tests running that are adding offices - await page.getByRole('option', { name: 'JPPSO Testy McTest' }).first().click(); + await page.getByRole('option', { name: 'PPPO Scott AFB - USAF' }).first().click(); await page.getByRole('button', { name: 'Save' }).click(); await adminPage.waitForPage.adminPage(); @@ -110,6 +110,161 @@ test.describe('Office User Create Page', () => { await expect(page.locator('#telephone')).toHaveValue('222-555-1234'); await expect(page.locator('#active')).toHaveText('Yes'); }); + + test('has correct supervisor role permissions', async ({ page, adminPage }) => { + await adminPage.signInAsNewAdminUser(); + // we tested the side nav in the previous test, + // so let's work with the assumption that we were already redirected to this page: + expect(page.url()).toContain('/system/requested-office-users'); + await page.getByRole('menuitem', { name: 'Office Users', exact: true }).click(); + expect(page.url()).toContain('/system/office-users'); + + await page.getByRole('link', { name: 'Create' }).click(); + await expect(page.getByRole('heading', { name: 'Create Office Users', exact: true })).toBeVisible(); + + expect(page.url()).toContain('/system/office-users/create'); + + // we need to add the date to the email so that it is unique every time (only one record per email allowed in db) + const testEmail = `cy.admin_user.${Date.now()}@example.com`; + + // create an office user + const firstName = page.getByLabel('First name'); + await firstName.focus(); + await firstName.fill('Cypress'); + + const lastName = page.getByLabel('Last name'); + await lastName.focus(); + await lastName.fill('Test'); + + const email = page.getByLabel('Email'); + await email.focus(); + await email.fill(testEmail); + + const phone = page.getByLabel('Telephone'); + await phone.focus(); + await phone.fill('222-555-1234'); + + // Define constants for all roles checkboxes to be tested + const customerCheckbox = page.getByLabel('Customer', { exact: true }); + const contractingOfficerCheckbox = page.getByLabel('Contracting Officer', { exact: true }); + const servicesCounselorCheckbox = page.getByLabel('Services Counselor', { exact: true }); + const primeSimulatorCheckbox = page.getByLabel('Prime Simulator', { exact: true }); + const qualityAssuranceEvaluatorCheckbox = page.getByLabel('Quality Assurance Evaluator', { exact: true }); + const customerServiceRepersentativeCheckbox = page.getByLabel('Customer Service Representative', { exact: true }); + const governmentSurveillanceRepresentativeCheckbox = page.getByLabel('Government Surveillance Representative', { + exact: true, + }); + const headquartersCheckbox = page.getByLabel('Headquarters', { exact: true }); + const taskOrderingOfficerCheckbox = page.getByLabel('Task Ordering Officer', { exact: true }); + const taskInvoicingOfficerCheckbox = page.getByLabel('Task Invoicing Officer', { exact: true }); + + // Define constants for privileges + const supervisorCheckbox = page.getByLabel('Supervisor', { exact: true }); + + // Check roles that cannot have supervisor priveleges + await customerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(customerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await customerCheckbox.click(); + await contractingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(contractingOfficerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await contractingOfficerCheckbox.click(); + await primeSimulatorCheckbox.click(); + await supervisorCheckbox.click(); + await expect(primeSimulatorCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await primeSimulatorCheckbox.click(); + await qualityAssuranceEvaluatorCheckbox.click(); + await supervisorCheckbox.click(); + await expect(qualityAssuranceEvaluatorCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await qualityAssuranceEvaluatorCheckbox.click(); + await customerServiceRepersentativeCheckbox.click(); + await supervisorCheckbox.click(); + await expect(customerServiceRepersentativeCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await customerServiceRepersentativeCheckbox.click(); + await governmentSurveillanceRepresentativeCheckbox.click(); + await supervisorCheckbox.click(); + await expect(governmentSurveillanceRepresentativeCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await governmentSurveillanceRepresentativeCheckbox.click(); + await headquartersCheckbox.click(); + await supervisorCheckbox.click(); + await expect(headquartersCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await headquartersCheckbox.click(); + + // Check roles that can have supervisor priveleges + await taskOrderingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(taskOrderingOfficerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).toBeChecked(); + await taskOrderingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await taskInvoicingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(taskInvoicingOfficerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).toBeChecked(); + await taskInvoicingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await servicesCounselorCheckbox.click(); + await supervisorCheckbox.click(); + await expect(servicesCounselorCheckbox).toBeChecked(); + await expect(supervisorCheckbox).toBeChecked(); + await servicesCounselorCheckbox.click(); + + // Check selecting roles after having supervisor selected for unallowed roles + await customerCheckbox.click(); + await expect(customerCheckbox).not.toBeChecked(); + await contractingOfficerCheckbox.click(); + await expect(contractingOfficerCheckbox).not.toBeChecked(); + await primeSimulatorCheckbox.click(); + await expect(primeSimulatorCheckbox).not.toBeChecked(); + await qualityAssuranceEvaluatorCheckbox.click(); + await expect(qualityAssuranceEvaluatorCheckbox).not.toBeChecked(); + await customerServiceRepersentativeCheckbox.click(); + await expect(customerServiceRepersentativeCheckbox).not.toBeChecked(); + await governmentSurveillanceRepresentativeCheckbox.click(); + await expect(governmentSurveillanceRepresentativeCheckbox).not.toBeChecked(); + await headquartersCheckbox.click(); + await expect(headquartersCheckbox).not.toBeChecked(); + + // Check selecting roles after having supervisor selected for allowed roles + await taskOrderingOfficerCheckbox.click(); + await expect(taskOrderingOfficerCheckbox).toBeChecked(); + await taskOrderingOfficerCheckbox.click(); + await taskInvoicingOfficerCheckbox.click(); + await expect(taskInvoicingOfficerCheckbox).toBeChecked(); + await taskInvoicingOfficerCheckbox.click(); + await servicesCounselorCheckbox.click(); + await expect(servicesCounselorCheckbox).toBeChecked(); + + // Continue test to ensure form still submits with valid information + // The autocomplete form results in multiple matching elements, so + // pick the input element + await page.getByLabel('Transportation Office').nth(0).fill('PPPO Scott AFB - USAF'); + // the autocomplete might return multiples because of concurrent + // tests running that are adding offices + await page.getByRole('option', { name: 'PPPO Scott AFB - USAF' }).first().click(); + + await page.getByRole('button', { name: 'Save' }).click(); + await adminPage.waitForPage.adminPage(); + + // redirected to edit details page + const officeUserID = await page.locator('#id').inputValue(); + + await expect(page.getByRole('heading', { name: `Office Users #${officeUserID}` })).toBeVisible(); + + await expect(page.locator('#email')).toHaveValue(testEmail); + await expect(page.locator('#firstName')).toHaveValue('Cypress'); + await expect(page.locator('#lastName')).toHaveValue('Test'); + await expect(page.locator('#telephone')).toHaveValue('222-555-1234'); + await expect(page.locator('#active')).toHaveText('Yes'); + }); }); test.describe('Office Users Show Page', () => { @@ -228,6 +383,178 @@ test.describe('Office Users Edit Page', () => { await expect(page.locator(`tr:has(:text("${email}")) >> td.column-lastName`)).toHaveText('NewLast'); }); + test('has correct supervisor role permissions', async ({ page, adminPage }) => { + const officeUser = await adminPage.testHarness.buildOfficeUserWithTOOAndTIO(); + const email = officeUser.okta_email; + + await adminPage.signInAsNewAdminUser(); + + expect(page.url()).toContain('/system/requested-office-users'); + await page.getByRole('menuitem', { name: 'Office Users', exact: true }).click(); + expect(page.url()).toContain('/system/office-users'); + await searchForOfficeUser(page, email); + await page.getByText(email).click(); + await adminPage.waitForPage.adminPage(); + + await page.getByRole('link', { name: 'Edit' }).click(); + await adminPage.waitForPage.adminPage(); + + const disabledFields = ['id', 'email', 'userId', 'createdAt', 'updatedAt']; + for (const field of disabledFields) { + await expect(page.locator(`#${field}`)).toBeDisabled(); + } + + const firstName = page.getByLabel('First name'); + await firstName.focus(); + await firstName.clear(); + await firstName.fill('NewFirst'); + + const lastName = page.getByLabel('Last name'); + await lastName.focus(); + await lastName.clear(); + await lastName.fill('NewLast'); + + // The autocomplete form results in multiple matching elements, so + // pick the input element + await expect(page.getByLabel('Transportation Office').nth(0)).toBeEditable(); + + // Add a Transportation Office Assignment + await page.getByTestId('addTransportationOfficeButton').click(); + // n = 2 because of the disabled GBLOC input + await expect(page.getByLabel('Transportation Office').nth(2)).toBeEditable(); + await page.getByLabel('Transportation Office').nth(2).fill('AGFM'); + // the autocomplete might return multiples because of concurrent + // tests running that are adding offices + await page.getByRole('option', { name: 'JPPSO - North East (AGFM) - USAF' }).first().click(); + // Set as primary transportation office + await page.getByLabel('Primary Office').nth(1).click(); + await page.getByText('You cannot designate more than one primary transportation office.'); + await page.getByLabel('Primary Office').nth(1).click(); + + // set the user to the active status they did NOT have before + const activeStatus = await page.locator('div:has(label :text-is("Active")) >> input[name="active"]').inputValue(); + + const newStatus = (activeStatus !== 'true').toString(); + await page.locator('div:has(label :text-is("Active")) >> #active').click(); + await page.locator(`ul[aria-labelledby="active-label"] >> li[data-value="${newStatus}"]`).click(); + + // Define constants for all roles checkboxes to be tested + const customerCheckbox = page.getByLabel('Customer', { exact: true }); + const contractingOfficerCheckbox = page.getByLabel('Contracting Officer', { exact: true }); + const servicesCounselorCheckbox = page.getByLabel('Services Counselor', { exact: true }); + const primeSimulatorCheckbox = page.getByLabel('Prime Simulator', { exact: true }); + const qualityAssuranceEvaluatorCheckbox = page.getByLabel('Quality Assurance Evaluator', { exact: true }); + const customerServiceRepersentativeCheckbox = page.getByLabel('Customer Service Representative', { exact: true }); + const governmentSurveillanceRepresentativeCheckbox = page.getByLabel('Government Surveillance Representative', { + exact: true, + }); + const headquartersCheckbox = page.getByLabel('Headquarters', { exact: true }); + const taskOrderingOfficerCheckbox = page.getByLabel('Task Ordering Officer', { exact: true }); + const taskInvoicingOfficerCheckbox = page.getByLabel('Task Invoicing Officer', { exact: true }); + + // Define constants for privileges + const supervisorCheckbox = page.getByLabel('Supervisor', { exact: true }); + + // Disable existing roles for testing + await taskOrderingOfficerCheckbox.click(); + await taskInvoicingOfficerCheckbox.click(); + + // Check roles that cannot have supervisor priveleges + await customerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(customerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await customerCheckbox.click(); + await contractingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(contractingOfficerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await contractingOfficerCheckbox.click(); + await primeSimulatorCheckbox.click(); + await supervisorCheckbox.click(); + await expect(primeSimulatorCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await primeSimulatorCheckbox.click(); + await qualityAssuranceEvaluatorCheckbox.click(); + await supervisorCheckbox.click(); + await expect(qualityAssuranceEvaluatorCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await qualityAssuranceEvaluatorCheckbox.click(); + await customerServiceRepersentativeCheckbox.click(); + await supervisorCheckbox.click(); + await expect(customerServiceRepersentativeCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await customerServiceRepersentativeCheckbox.click(); + await governmentSurveillanceRepresentativeCheckbox.click(); + await supervisorCheckbox.click(); + await expect(governmentSurveillanceRepresentativeCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await governmentSurveillanceRepresentativeCheckbox.click(); + await headquartersCheckbox.click(); + await supervisorCheckbox.click(); + await expect(headquartersCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await headquartersCheckbox.click(); + + // Check roles that can have supervisor priveleges + await taskOrderingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(taskOrderingOfficerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).toBeChecked(); + await taskOrderingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await taskInvoicingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(taskInvoicingOfficerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).toBeChecked(); + await taskInvoicingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await servicesCounselorCheckbox.click(); + await supervisorCheckbox.click(); + await expect(servicesCounselorCheckbox).toBeChecked(); + await expect(supervisorCheckbox).toBeChecked(); + await servicesCounselorCheckbox.click(); + + // Check selecting roles after having supervisor selected for unallowed roles + await customerCheckbox.click(); + await expect(customerCheckbox).not.toBeChecked(); + await contractingOfficerCheckbox.click(); + await expect(contractingOfficerCheckbox).not.toBeChecked(); + await primeSimulatorCheckbox.click(); + await expect(primeSimulatorCheckbox).not.toBeChecked(); + await qualityAssuranceEvaluatorCheckbox.click(); + await expect(qualityAssuranceEvaluatorCheckbox).not.toBeChecked(); + await customerServiceRepersentativeCheckbox.click(); + await expect(customerServiceRepersentativeCheckbox).not.toBeChecked(); + await governmentSurveillanceRepresentativeCheckbox.click(); + await expect(governmentSurveillanceRepresentativeCheckbox).not.toBeChecked(); + await headquartersCheckbox.click(); + await expect(headquartersCheckbox).not.toBeChecked(); + + // Check selecting roles after having supervisor selected for allowed roles + await taskOrderingOfficerCheckbox.click(); + await expect(taskOrderingOfficerCheckbox).toBeChecked(); + await taskOrderingOfficerCheckbox.click(); + await taskInvoicingOfficerCheckbox.click(); + await expect(taskInvoicingOfficerCheckbox).toBeChecked(); + await taskInvoicingOfficerCheckbox.click(); + await servicesCounselorCheckbox.click(); + await expect(servicesCounselorCheckbox).toBeChecked(); + + // Continue test to ensure form still submits with valid information + await page.getByRole('button', { name: 'Save' }).click(); + await adminPage.waitForPage.adminPage(); + + await searchForOfficeUser(page, email); + await expect(page.locator(`tr:has(:text("${email}")) >> td.column-active >> svg`)).toHaveAttribute( + 'data-testid', + newStatus, + ); + + await expect(page.locator(`tr:has(:text("${email}")) >> td.column-firstName`)).toHaveText('NewFirst'); + await expect(page.locator(`tr:has(:text("${email}")) >> td.column-lastName`)).toHaveText('NewLast'); + }); + test('prevents safety move priv selection with Customer role', async ({ page, adminPage }) => { const officeUser = await adminPage.testHarness.buildOfficeUserWithCustomer(); const email = officeUser.okta_email; diff --git a/src/components/Admin/ImportOfficeUserButton/validation.test.js b/src/components/Admin/ImportOfficeUserButton/validation.test.js index 379cb8a7be6..7639ea9876f 100644 --- a/src/components/Admin/ImportOfficeUserButton/validation.test.js +++ b/src/components/Admin/ImportOfficeUserButton/validation.test.js @@ -1,6 +1,7 @@ -import { checkRequiredFields, checkTelephone, parseRoles } from './validation'; +import { checkRequiredFields, checkTelephone, parseRoles, parsePrivileges } from './validation'; import { adminOfficeRoles, roleTypes } from 'constants/userRoles'; +import { elevatedPrivilegeTypes } from 'constants/userPrivileges'; describe('checkRequiredFields', () => { it('success: does nothing if all fields provided', () => { @@ -71,3 +72,36 @@ describe('parseRoles', () => { expect(parseInvalidRoles).toThrowError('Invalid roles provided for row.'); }); }); + +describe('parsePrivileges', () => { + const supervisorPrivilege = { privilegeType: 'supervisor', name: 'Supervisor' }; + const safetyPrivilege = { privilegeType: 'safety', name: 'Safety Moves' }; + + it('fail: throws an error if there are no privileges', () => { + function parseEmptyPrivileges() { + parsePrivileges(''); + } + expect(parseEmptyPrivileges).toThrowError('Processing Error: Unable to parse privileges for row.'); + }); + + it('success: parses one privilege into an array of len 1', () => { + const privileges = elevatedPrivilegeTypes.SUPERVISOR; + const privilegesArray = parsePrivileges(privileges); + expect(privilegesArray).toHaveLength(1); + expect(privilegesArray).toContainEqual(supervisorPrivilege); + }); + + it('success: parses multiple privileges into an array', () => { + const privileges = `${elevatedPrivilegeTypes.SUPERVISOR}, ${elevatedPrivilegeTypes.SAFETY}`; + const privilegesArray = parsePrivileges(privileges); + expect(privilegesArray).toHaveLength(2); + expect(privilegesArray).toEqual(expect.arrayContaining([supervisorPrivilege, safetyPrivilege])); + }); + + it('fail: throws an error if there is an invalid privilege', () => { + function parseInvalidPrivileges() { + parsePrivileges('test_privilege'); + } + expect(parseInvalidPrivileges).toThrowError('Invalid privileges provided for row.'); + }); +}); diff --git a/src/components/Customer/Review/ShipmentCard/NTSRShipmentCard/NTSRShipmentCard.test.jsx b/src/components/Customer/Review/ShipmentCard/NTSRShipmentCard/NTSRShipmentCard.test.jsx index 4a08a029138..cdddbd01ad0 100644 --- a/src/components/Customer/Review/ShipmentCard/NTSRShipmentCard/NTSRShipmentCard.test.jsx +++ b/src/components/Customer/Review/ShipmentCard/NTSRShipmentCard/NTSRShipmentCard.test.jsx @@ -14,7 +14,7 @@ const defaultProps = { onDeleteClick: jest.fn(), onIncompleteClick: jest.fn(), showEditAndDeleteBtn: false, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', shipmentId: '#ABC123K', requestedDeliveryDate: new Date('03/01/2020').toISOString(), destinationZIP: '73523', @@ -35,7 +35,7 @@ const completeProps = { onDeleteClick: jest.fn(), onIncompleteClick: jest.fn(), showEditAndDeleteBtn: false, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', shipmentId: 'ABC123K', shipmentLocator: 'ABC123K-01', status: shipmentStatuses.SUBMITTED, @@ -49,7 +49,7 @@ const incompleteProps = { onDeleteClick: jest.fn(), onIncompleteClick: mockedOnIncompleteClickFunction, showEditAndDeleteBtn: false, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', shipmentId: 'ABC123K', shipmentLocator: 'ABC123K-01', status: shipmentStatuses.DRAFT, diff --git a/src/components/Customer/Review/ShipmentCard/ShipmentCard.stories.jsx b/src/components/Customer/Review/ShipmentCard/ShipmentCard.stories.jsx index d077b115917..15a0608bb25 100644 --- a/src/components/Customer/Review/ShipmentCard/ShipmentCard.stories.jsx +++ b/src/components/Customer/Review/ShipmentCard/ShipmentCard.stories.jsx @@ -84,7 +84,7 @@ const ntsDefaultProps = { const ntsrDefaultProps = { moveId: 'testMove123', shipmentNumber: 1, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', shipmentId: 'ABC123K', showEditAndDeleteBtn: true, onEditClick: noop, diff --git a/src/components/Office/AddOrdersForm/AddOrdersForm.jsx b/src/components/Office/AddOrdersForm/AddOrdersForm.jsx index ca0b173a684..113d8761284 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'; @@ -39,6 +39,9 @@ const AddOrdersForm = ({ 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() @@ -95,9 +98,24 @@ const AddOrdersForm = ({ } }, [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; @@ -107,17 +125,37 @@ const AddOrdersForm = ({ 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 (
@@ -127,8 +165,12 @@ const AddOrdersForm = ({ { + handleChange(e); + handleOrderTypeChange(e); + }} isDisabled={isSafetyMoveSelected || isBluebarkMoveSelected} /> @@ -206,6 +248,7 @@ const AddOrdersForm = ({ onChange={(e) => { handleHasDependentsChange(e); }} + disabled={isHasDependentsDisabled} /> { handleHasDependentsChange(e); }} + disabled={isHasDependentsDisabled} /> diff --git a/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx b/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx index 611331d78a9..a374c86022b 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,6 +228,144 @@ 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); + + 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); + + 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); + + 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); + + 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(); + }); + }); +}); + describe('AddOrdersForm - Edge Cases and Additional Scenarios', () => { it('disables orders type when safety move is selected', async () => { render( diff --git a/src/components/Office/BillableWeight/BillableWeightCard/BillableWeightCard.test.jsx b/src/components/Office/BillableWeight/BillableWeightCard/BillableWeightCard.test.jsx index 8cd2618257b..64704156e16 100644 --- a/src/components/Office/BillableWeight/BillableWeightCard/BillableWeightCard.test.jsx +++ b/src/components/Office/BillableWeight/BillableWeightCard/BillableWeightCard.test.jsx @@ -224,7 +224,7 @@ describe('BillableWeightCard', () => { const shipments = [ { id: '0001', - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', calculatedBillableWeight: 1000, estimatedWeight: 5600, ntsRecordedWeight: 1234, @@ -257,7 +257,7 @@ describe('BillableWeightCard', () => { const shipments = [ { id: '0001', - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', calculatedBillableWeight: 1000, estimatedWeight: 5600, ntsRecordedWeight: 4000, @@ -265,7 +265,7 @@ describe('BillableWeightCard', () => { }, { id: '0002', - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', calculatedBillableWeight: 3200, estimatedWeight: 5000, ntsRecordedWeight: 500, diff --git a/src/components/Office/EvaluationReportPreview/EvaluationReportPreview.test.jsx b/src/components/Office/EvaluationReportPreview/EvaluationReportPreview.test.jsx index c2fdc7afb7e..9abe1730ded 100644 --- a/src/components/Office/EvaluationReportPreview/EvaluationReportPreview.test.jsx +++ b/src/components/Office/EvaluationReportPreview/EvaluationReportPreview.test.jsx @@ -165,7 +165,7 @@ const mtoShipments = [ streetAddress2: 'P.O. Box 12345', streetAddress3: 'c/o Some Person', }, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', status: 'DRAFT', storageFacility: { address: { diff --git a/src/components/Office/ExternalVendorWeightSummary/ExternalVendorWeightSummary.stories.jsx b/src/components/Office/ExternalVendorWeightSummary/ExternalVendorWeightSummary.stories.jsx index 714fd21cde5..6e0469c9ce2 100644 --- a/src/components/Office/ExternalVendorWeightSummary/ExternalVendorWeightSummary.stories.jsx +++ b/src/components/Office/ExternalVendorWeightSummary/ExternalVendorWeightSummary.stories.jsx @@ -21,15 +21,15 @@ export const WithMultipleNTSRShipments = () => ( shipments={[ { ntsRecordedWeight: 1000, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', }, { ntsRecordedWeight: 2000, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', }, { ntsRecordedWeight: 1500, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', }, ]} /> @@ -57,18 +57,18 @@ export const WithMultipleShipmentsOfBothTypes = () => ( shipments={[ { ntsRecordedWeight: 1000, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', }, { ntsRecordedWeight: 2000, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', }, { shipmentType: 'HHG_INTO_NTS', }, { ntsRecordedWeight: 1500, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', }, { shipmentType: 'HHG_INTO_NTS', @@ -92,7 +92,7 @@ export const WithOneNTSRShipment = () => ( shipments={[ { ntsRecordedWeight: 1500, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', }, ]} /> diff --git a/src/components/Office/ExternalVendorWeightSummary/ExternalVendorWeightSummary.test.jsx b/src/components/Office/ExternalVendorWeightSummary/ExternalVendorWeightSummary.test.jsx index e6ea97829e9..90cce51be2f 100644 --- a/src/components/Office/ExternalVendorWeightSummary/ExternalVendorWeightSummary.test.jsx +++ b/src/components/Office/ExternalVendorWeightSummary/ExternalVendorWeightSummary.test.jsx @@ -30,9 +30,7 @@ describe('ExternalVendorWeightSummary component', () => { it('renders with one NTSR shipment', () => { render( - + , ); @@ -42,9 +40,9 @@ describe('ExternalVendorWeightSummary component', () => { it('renders with many NTSR shipments', () => { const shipments = [ - { ntsRecordedWeight: 1000, shipmentType: 'HHG_OUTOF_NTS_DOMESTIC' }, - { ntsRecordedWeight: 500, shipmentType: 'HHG_OUTOF_NTS_DOMESTIC' }, - { ntsRecordedWeight: 1500, shipmentType: 'HHG_OUTOF_NTS_DOMESTIC' }, + { ntsRecordedWeight: 1000, shipmentType: 'HHG_OUTOF_NTS' }, + { ntsRecordedWeight: 500, shipmentType: 'HHG_OUTOF_NTS' }, + { ntsRecordedWeight: 1500, shipmentType: 'HHG_OUTOF_NTS' }, ]; render( @@ -58,11 +56,11 @@ describe('ExternalVendorWeightSummary component', () => { it('renders with many NTSR and NTS shipments', () => { const shipments = [ - { ntsRecordedWeight: 1000, shipmentType: 'HHG_OUTOF_NTS_DOMESTIC' }, + { ntsRecordedWeight: 1000, shipmentType: 'HHG_OUTOF_NTS' }, { shipmentType: 'HHG_INTO_NTS' }, - { ntsRecordedWeight: 500, shipmentType: 'HHG_OUTOF_NTS_DOMESTIC' }, + { ntsRecordedWeight: 500, shipmentType: 'HHG_OUTOF_NTS' }, { shipmentType: 'HHG_INTO_NTS' }, - { ntsRecordedWeight: 1500, shipmentType: 'HHG_OUTOF_NTS_DOMESTIC' }, + { ntsRecordedWeight: 1500, shipmentType: 'HHG_OUTOF_NTS' }, ]; render( diff --git a/src/components/Office/PPM/ReviewShipmentWeightsTable/helpers.test.jsx b/src/components/Office/PPM/ReviewShipmentWeightsTable/helpers.test.jsx index d6feaca828c..84bbf650279 100644 --- a/src/components/Office/PPM/ReviewShipmentWeightsTable/helpers.test.jsx +++ b/src/components/Office/PPM/ReviewShipmentWeightsTable/helpers.test.jsx @@ -30,7 +30,7 @@ describe('addShipmentNumbersToTableData', () => { describe('determineTableRowClassname', () => { it.each([ - ['HHG_OUTOF_NTS_DOMESTIC', styles[`review-shipment-weights-table-row-NTS-release`]], + ['HHG_OUTOF_NTS', styles[`review-shipment-weights-table-row-NTS-release`]], ['HHG_INTO_NTS', styles[`review-shipment-weights-table-row-NTS`]], ['PPM', styles[`review-shipment-weights-table-row-PPM`]], ['HHG', styles[`review-shipment-weights-table-row-HHG`]], @@ -43,7 +43,7 @@ describe('determineTableRowClassname', () => { describe('shipmentTypeCellDisplayHelper', () => { it.each([ [{ shipmentType: 'PPM', showNumber: false }, 'PPM'], - [{ shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', showNumber: true, shipmentNumber: 123 }, 'NTS-release 123'], + [{ shipmentType: 'HHG_OUTOF_NTS', showNumber: true, shipmentNumber: 123 }, 'NTS-release 123'], [{ shipmentType: 'HHG', showNumber: true, shipmentNumber: 8 }, 'HHG 8'], ])('renders the correct Shipment Type Cell', (row, expectedResult) => { render(); @@ -53,7 +53,7 @@ describe('shipmentTypeCellDisplayHelper', () => { describe('estimatedWeightDisplayHelper', () => { it.each([ - [{ shipmentType: 'HHG_OUTOF_NTS_DOMESTIC' }, 'N/A'], + [{ shipmentType: 'HHG_OUTOF_NTS' }, 'N/A'], [{ shipmentType: 'HHG_INTO_NTS', ntsRecordedWeight: 1234, primeEstimatedWeight: 9876 }, '1,234 lbs'], [{ shipmentType: 'HHG', ntsRecordedWeight: 1234, primeEstimatedWeight: 9876 }, '9,876 lbs'], [{ shipmentType: 'HHG', primeEstimatedWeight: 0 }, DASH], @@ -64,7 +64,7 @@ describe('estimatedWeightDisplayHelper', () => { describe('actualWeightDisplayHelper', () => { it.each([ - [{ shipmentType: 'HHG_OUTOF_NTS_DOMESTIC' }, DASH], + [{ shipmentType: 'HHG_OUTOF_NTS' }, DASH], [{ primeActualWeight: 1234 }, '1,234 lbs'], [{ reweigh: { weight: 9876 } }, '9,876 lbs'], [{ primeActualWeight: 1234, reweigh: { weight: 9876 } }, '1,234 lbs'], diff --git a/src/components/Office/PortTable/PortTable.jsx b/src/components/Office/PortTable/PortTable.jsx new file mode 100644 index 00000000000..49635850195 --- /dev/null +++ b/src/components/Office/PortTable/PortTable.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import classnames from 'classnames'; + +import DataTableWrapper from '../../DataTableWrapper/index'; +import DataTable from '../../DataTable/index'; +import styles from '../ShipmentAddresses/ShipmentAddresses.module.scss'; + +import { formatPortInfo } from 'utils/formatters'; + +const PortTable = ({ poeLocation, podLocation }) => { + return ( + + + + ); +}; + +export default PortTable; diff --git a/src/components/Office/PortTable/PortTable.stories.jsx b/src/components/Office/PortTable/PortTable.stories.jsx new file mode 100644 index 00000000000..d3c368cc53c --- /dev/null +++ b/src/components/Office/PortTable/PortTable.stories.jsx @@ -0,0 +1,46 @@ +import React from 'react'; + +import PortTable from './PortTable'; + +const poeLocationSet = { + poeLocation: { + portCode: 'PDX', + portName: 'PORTLAND INTL', + city: 'PORTLAND', + state: 'OREGON', + zip: '97220', + }, + podLocation: null, +}; + +const podLocationSet = { + poeLocation: null, + podLocation: { + portCode: 'SEA', + portName: 'SEATTLE TACOMA INTL', + city: 'SEATTLE', + state: 'WASHINGTON', + zip: '98158', + }, +}; + +export default { + title: 'Office Components / PortTable', + component: PortTable, +}; + +export const standard = () => { + return ( +
+ +
+ ); +}; + +export const podLocationDisplay = () => { + return ( +
+ +
+ ); +}; diff --git a/src/components/Office/PortTable/PortTable.test.jsx b/src/components/Office/PortTable/PortTable.test.jsx new file mode 100644 index 00000000000..5dd19449aef --- /dev/null +++ b/src/components/Office/PortTable/PortTable.test.jsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; + +import PortTable from './PortTable'; + +const poeLocationSet = { + poeLocation: { + portCode: 'PDX', + portName: 'PORTLAND INTL', + city: 'PORTLAND', + state: 'OREGON', + zip: '97220', + }, + podLocation: null, +}; + +const podLocationSet = { + poeLocation: null, + podLocation: { + portCode: 'SEA', + portName: 'SEATTLE TACOMA INTL', + city: 'SEATTLE', + state: 'WASHINGTON', + zip: '98158', + }, +}; + +const nullPortLocation = { + poeLocation: null, + podLocation: null, +}; + +describe('PortTable', () => { + it('renders POE location if poeLocation is set', async () => { + render(); + expect(screen.getByText(/PDX - PORTLAND INTL/)).toBeInTheDocument(); + expect(screen.getByText(/Portland, Oregon 97220/)).toBeInTheDocument(); + expect(screen.queryByText(/SEA - SEATTLE TACOMA INTL/)).not.toBeInTheDocument(); + expect(screen.queryByText(/Seattle, Washington 98158/)).not.toBeInTheDocument(); + }); + + it('renders POD location if podLocation is set', async () => { + render(); + expect(screen.queryByText(/PDX - PORTLAND INTL/)).not.toBeInTheDocument(); + expect(screen.queryByText(/Portland, Oregon 97220/)).not.toBeInTheDocument(); + expect(screen.getByText(/SEA - SEATTLE TACOMA INTL/)).toBeInTheDocument(); + expect(screen.getByText(/Seattle, Washington 98158/)).toBeInTheDocument(); + }); + + it('does not render port information if poeLocation and podLocation are null', async () => { + render(); + expect(screen.queryByText(/PDX - PORTLAND INTL/)).not.toBeInTheDocument(); + expect(screen.queryByText(/Portland, Oregon 97220/)).not.toBeInTheDocument(); + expect(screen.queryByText(/SEA - SEATTLE TACOMA INTL/)).not.toBeInTheDocument(); + expect(screen.queryByText(/Seattle, Washington 98158/)).not.toBeInTheDocument(); + }); +}); diff --git a/src/components/Office/RequestedShipments/ApprovedRequestedShipments.jsx b/src/components/Office/RequestedShipments/ApprovedRequestedShipments.jsx index d29fb37f0dd..ea7b435d49b 100644 --- a/src/components/Office/RequestedShipments/ApprovedRequestedShipments.jsx +++ b/src/components/Office/RequestedShipments/ApprovedRequestedShipments.jsx @@ -25,12 +25,12 @@ import { isBooleanFlagEnabled } from 'utils/featureFlags'; // Different things show when collapsed depending on if the shipment is an external vendor or not. const showWhenCollapsedWithExternalVendor = { HHG_INTO_NTS: ['serviceOrderNumber', 'requestedDeliveryDate'], - HHG_OUTOF_NTS_DOMESTIC: ['serviceOrderNumber', 'requestedPickupDate'], + HHG_OUTOF_NTS: ['serviceOrderNumber', 'requestedPickupDate'], }; const showWhenCollapsedWithGHCPrime = { HHG_INTO_NTS: ['tacType', 'requestedDeliveryDate'], - HHG_OUTOF_NTS_DOMESTIC: ['ntsRecordedWeight', 'serviceOrderNumber', 'tacType', 'requestedPickupDate'], + HHG_OUTOF_NTS: ['ntsRecordedWeight', 'serviceOrderNumber', 'tacType', 'requestedPickupDate'], }; const errorIfMissing = [ diff --git a/src/components/Office/RequestedShipments/SubmittedRequestedShipments.jsx b/src/components/Office/RequestedShipments/SubmittedRequestedShipments.jsx index b6299fd404f..69793103aae 100644 --- a/src/components/Office/RequestedShipments/SubmittedRequestedShipments.jsx +++ b/src/components/Office/RequestedShipments/SubmittedRequestedShipments.jsx @@ -35,12 +35,12 @@ import { updateMTOShipment } from 'services/ghcApi'; // Different things show when collapsed depending on if the shipment is an external vendor or not. const showWhenCollapsedWithExternalVendor = { HHG_INTO_NTS: ['serviceOrderNumber', 'requestedDeliveryDate'], - HHG_OUTOF_NTS_DOMESTIC: ['serviceOrderNumber', 'requestedPickupDate'], + HHG_OUTOF_NTS: ['serviceOrderNumber', 'requestedPickupDate'], }; const showWhenCollapsedWithGHCPrime = { HHG_INTO_NTS: ['tacType', 'requestedDeliveryDate'], - HHG_OUTOF_NTS_DOMESTIC: ['ntsRecordedWeight', 'serviceOrderNumber', 'tacType', 'requestedPickupDate'], + HHG_OUTOF_NTS: ['ntsRecordedWeight', 'serviceOrderNumber', 'tacType', 'requestedPickupDate'], }; const SubmittedRequestedShipments = ({ diff --git a/src/components/Office/ShipmentDetails/ShipmentDetails.stories.jsx b/src/components/Office/ShipmentDetails/ShipmentDetails.stories.jsx index 107bed9636c..15d814d367a 100644 --- a/src/components/Office/ShipmentDetails/ShipmentDetails.stories.jsx +++ b/src/components/Office/ShipmentDetails/ShipmentDetails.stories.jsx @@ -109,6 +109,13 @@ const shipment = { serviceOrderNumber: '1234', tacType: LOA_TYPE.HHG, sacType: LOA_TYPE.NTS, + poeLocation: { + portCode: 'PDX', + portName: 'PORTLAND INTL', + city: 'PORTLAND', + state: 'OREGON', + zip: '97220', + }, }; const order = { diff --git a/src/components/Office/ShipmentDetails/ShipmentDetailsMain.jsx b/src/components/Office/ShipmentDetails/ShipmentDetailsMain.jsx index 430d0808f60..6c1233745a7 100644 --- a/src/components/Office/ShipmentDetails/ShipmentDetailsMain.jsx +++ b/src/components/Office/ShipmentDetails/ShipmentDetailsMain.jsx @@ -15,6 +15,7 @@ import ConvertSITToCustomerExpenseModal from 'components/Office/ConvertSITToCust import ShipmentSITDisplay from 'components/Office/ShipmentSITDisplay/ShipmentSITDisplay'; import ImportantShipmentDates from 'components/Office/ImportantShipmentDates/ImportantShipmentDates'; import ShipmentAddresses from 'components/Office/ShipmentAddresses/ShipmentAddresses'; +import PortTable from 'components/Office/PortTable/PortTable'; import ShipmentWeightDetails from 'components/Office/ShipmentWeightDetails/ShipmentWeightDetails'; import ShipmentRemarks from 'components/Office/ShipmentRemarks/ShipmentRemarks'; import Restricted from 'components/Restricted/Restricted'; @@ -67,6 +68,8 @@ const ShipmentDetailsMain = ({ storageInTransit, shipmentType, storageFacility, + poeLocation, + podLocation, } = shipment; const { originDutyLocationAddress, destinationDutyLocationAddress } = dutyLocationAddresses; @@ -135,6 +138,8 @@ const ShipmentDetailsMain = ({ let pickupActualDate; let plannedMoveDate; let actualMoveDate; + let displayPoeLocation; + let displayPodLocation; switch (shipmentType) { case SHIPMENT_OPTIONS.HHG: @@ -144,6 +149,8 @@ const ShipmentDetailsMain = ({ weightResult = primeEstimatedWeight; displayedPickupAddress = pickupAddress; displayedDeliveryAddress = destinationAddress || destinationDutyLocationAddress; + displayPoeLocation = poeLocation; + displayPodLocation = podLocation; break; case SHIPMENT_OPTIONS.NTS: pickupRequestedDate = requestedPickupDate; @@ -175,6 +182,8 @@ const ShipmentDetailsMain = ({ weightResult = primeEstimatedWeight; displayedPickupAddress = pickupAddress; displayedDeliveryAddress = destinationAddress || destinationDutyLocationAddress; + displayPoeLocation = poeLocation; + displayPodLocation = podLocation; break; } @@ -261,6 +270,9 @@ const ShipmentDetailsMain = ({ handleShowDiversionModal={handleShowDiversionModal} isMoveLocked={isMoveLocked} /> + {(displayPoeLocation || displayPodLocation) && ( + + )} { expect(screen.queryByText(formatDateWithUTC('2024-01-01'))).toBeInTheDocument(); }); + +describe('Shipment Details Main - PortTable', () => { + const poeLocation = { + portCode: 'PDX', + portName: 'PORTLAND INTL', + city: 'PORTLAND', + state: 'OREGON', + zip: '97220', + }; + + const podLocation = { + portCode: 'SEA', + portName: 'SEATTLE TACOMA INTL', + city: 'SEATTLE', + state: 'WASHINGTON', + zip: '98158', + }; + + it('displays PortTable when poeLocation is provided', () => { + const shipmentWithPOELocation = { + ...noSITShipment, + poeLocation, + podLocation: null, + }; + + render( + + + , + ); + + expect(screen.getByText('Port of Embarkation')).toBeInTheDocument(); + expect(screen.getByText('Port of Debarkation')).toBeInTheDocument(); + }); + + it('displays PortTable when podLocation is provided', () => { + const shipmentWithPODLocation = { + ...noSITShipment, + poeLocation: null, + podLocation, + }; + + render( + + + , + ); + + expect(screen.getByText('Port of Embarkation')).toBeInTheDocument(); + expect(screen.getByText('Port of Debarkation')).toBeInTheDocument(); + }); + + it('does not display PortTable when poeLocation and podLocation are not provided', () => { + const shipmentWithNoPortLocation = { + ...noSITShipment, + poeLocation: null, + podLocation: null, + }; + + render( + + + , + ); + + expect(screen.queryByText('Port of Embarkation')).not.toBeInTheDocument(); + expect(screen.queryByText('Port of Debarkation')).not.toBeInTheDocument(); + }); +}); diff --git a/src/components/Office/ShipmentServiceItemsTable/ShipmentServiceItemsTable.jsx b/src/components/Office/ShipmentServiceItemsTable/ShipmentServiceItemsTable.jsx index 2f2410a2b6b..57949441642 100644 --- a/src/components/Office/ShipmentServiceItemsTable/ShipmentServiceItemsTable.jsx +++ b/src/components/Office/ShipmentServiceItemsTable/ShipmentServiceItemsTable.jsx @@ -27,7 +27,7 @@ const shipmentTypes = { serviceItemCodes.DDP, serviceItemCodes.DNPK, ], - HHG_OUTOF_NTS_DOMESTIC: [ + HHG_OUTOF_NTS: [ serviceItemCodes.DLH, serviceItemCodes.DSH, serviceItemCodes.FSC, diff --git a/src/content/shipments.js b/src/content/shipments.js index 85c146b7750..583865cf9f9 100644 --- a/src/content/shipments.js +++ b/src/content/shipments.js @@ -29,6 +29,6 @@ export const shipmentSectionLabels = { BOAT_TOW_AWAY: 'Boat Tow Away shipment', MOBILE_HOME: 'Mobile Home shipment', HHG_INTO_NTS: 'NTS shipment', - HHG_OUTOF_NTS_DOMESTIC: 'NTS-release shipment', + HHG_OUTOF_NTS: 'NTS-release shipment', UNACCOMPANIED_BAGGAGE: 'UB shipment', }; diff --git a/src/hooks/queries.test.jsx b/src/hooks/queries.test.jsx index 4136a50f825..86679de027e 100644 --- a/src/hooks/queries.test.jsx +++ b/src/hooks/queries.test.jsx @@ -67,7 +67,7 @@ jest.mock('services/ghcApi', () => ({ ], }, b2: { - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', mtoAgents: [ { agentType: 'RELEASING_AGENT', @@ -115,7 +115,7 @@ jest.mock('services/ghcApi', () => ({ }, { id: 'b2', - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', mtoAgents: [ { agentType: 'RELEASING_AGENT', @@ -449,7 +449,7 @@ describe('usePaymentRequestQueries', () => { }, { id: 'b2', - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', mtoAgents: [ { agentType: 'RELEASING_AGENT', @@ -1123,7 +1123,7 @@ describe('useReviewShipmentWeightsQuery', () => { }, { id: 'b2', - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', mtoAgents: [ { agentType: 'RELEASING_AGENT', diff --git a/src/pages/Office/MoveDetails/MoveDetails.jsx b/src/pages/Office/MoveDetails/MoveDetails.jsx index 281a4618a19..2032009100a 100644 --- a/src/pages/Office/MoveDetails/MoveDetails.jsx +++ b/src/pages/Office/MoveDetails/MoveDetails.jsx @@ -73,7 +73,7 @@ const MoveDetails = ({ // eslint-disable-next-line react-hooks/exhaustive-deps const errorIfMissing = { HHG_INTO_NTS: [{ fieldName: 'storageFacility' }, { fieldName: 'serviceOrderNumber' }, { fieldName: 'tacType' }], - HHG_OUTOF_NTS_DOMESTIC: [ + HHG_OUTOF_NTS: [ { fieldName: 'storageFacility' }, { fieldName: 'ntsRecordedWeight' }, { fieldName: 'serviceOrderNumber' }, @@ -115,7 +115,7 @@ const MoveDetails = ({ if (isRetirementOrSeparation) { // destination type must be set for for HHG, NTSR shipments only errorIfMissing.HHG = [{ fieldName: 'destinationType' }]; - errorIfMissing.HHG_OUTOF_NTS_DOMESTIC.push({ fieldName: 'destinationType' }); + errorIfMissing.HHG_OUTOF_NTS.push({ fieldName: 'destinationType' }); } let sections = useMemo(() => { diff --git a/src/pages/Office/MoveDetails/MoveDetails.test.jsx b/src/pages/Office/MoveDetails/MoveDetails.test.jsx index 426e83c5e8c..58755c17988 100644 --- a/src/pages/Office/MoveDetails/MoveDetails.test.jsx +++ b/src/pages/Office/MoveDetails/MoveDetails.test.jsx @@ -677,7 +677,7 @@ const requestedMoveDetailsMissingInfoQuery = { }, requestedPickupDate: '2018-03-15', scheduledPickupDate: '2018-03-16', - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', status: 'SUBMITTED', updatedAt: '2020-06-10T15:58:02.404031Z', }, diff --git a/src/pages/Office/MoveQueue/MoveQueue.jsx b/src/pages/Office/MoveQueue/MoveQueue.jsx index bc156ffb44c..95b703189fc 100644 --- a/src/pages/Office/MoveQueue/MoveQueue.jsx +++ b/src/pages/Office/MoveQueue/MoveQueue.jsx @@ -178,6 +178,9 @@ export const columns = (moveLockFlag, isQueueManagementEnabled, showBranchFilter { id: 'assignedTo', isFilterable: true, + exportValue: (row) => { + return row.assignedTo ? `${row.assignedTo?.lastName}, ${row.assignedTo?.firstName}` : ''; + }, }, ), ); diff --git a/src/pages/Office/MoveTaskOrder/moveTaskOrderUnitTestData.js b/src/pages/Office/MoveTaskOrder/moveTaskOrderUnitTestData.js index ef6868c9877..614867fe84b 100644 --- a/src/pages/Office/MoveTaskOrder/moveTaskOrderUnitTestData.js +++ b/src/pages/Office/MoveTaskOrder/moveTaskOrderUnitTestData.js @@ -2292,7 +2292,7 @@ export const reviewWeightsQuery = { streetAddress2: 'P.O. Box 12345', streetAddress3: 'c/o Some Person', }, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', status: 'DRAFT', updatedAt: '2023-02-28T18:02:59.896Z', }, @@ -2742,7 +2742,7 @@ export const reviewWeightsNoProGearQuery = { streetAddress2: 'P.O. Box 12345', streetAddress3: 'c/o Some Person', }, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', status: 'DRAFT', updatedAt: '2023-02-28T18:02:59.896Z', }, diff --git a/src/pages/Office/PaymentRequestQueue/PaymentRequestQueue.jsx b/src/pages/Office/PaymentRequestQueue/PaymentRequestQueue.jsx index 2edaeb880e5..a46518a2627 100644 --- a/src/pages/Office/PaymentRequestQueue/PaymentRequestQueue.jsx +++ b/src/pages/Office/PaymentRequestQueue/PaymentRequestQueue.jsx @@ -181,6 +181,9 @@ export const columns = (moveLockFlag, isQueueManagementEnabled, showBranchFilter { id: 'assignedTo', isFilterable: true, + exportValue: (row) => { + return row.assignedTo ? `${row.assignedTo?.lastName}, ${row.assignedTo?.firstName}` : ''; + }, }, ), ); diff --git a/src/pages/Office/ReviewBillableWeight/ReviewBillableWeight.test.jsx b/src/pages/Office/ReviewBillableWeight/ReviewBillableWeight.test.jsx index 21bfd2ba795..9247b13a38d 100644 --- a/src/pages/Office/ReviewBillableWeight/ReviewBillableWeight.test.jsx +++ b/src/pages/Office/ReviewBillableWeight/ReviewBillableWeight.test.jsx @@ -148,7 +148,7 @@ const mockMtoNTSReleaseShipments = [ { id: 1, status: shipmentStatuses.APPROVED, - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', calculatedBillableWeight: 3000, billableWeightCap: 1000, primeEstimatedWeight: 1000, diff --git a/src/pages/Office/ServicesCounselingMoveDetails/ServicesCounselingMoveDetails.jsx b/src/pages/Office/ServicesCounselingMoveDetails/ServicesCounselingMoveDetails.jsx index 9cbe2fa5f85..7ab63c0a024 100644 --- a/src/pages/Office/ServicesCounselingMoveDetails/ServicesCounselingMoveDetails.jsx +++ b/src/pages/Office/ServicesCounselingMoveDetails/ServicesCounselingMoveDetails.jsx @@ -106,15 +106,15 @@ const ServicesCounselingMoveDetails = ({ // ntsr defaults shows preferred delivery date, storage facility address, delivery address, flagged items when collapsed const showWhenCollapsed = { HHG_INTO_NTS: ['counselorRemarks'], - HHG_OUTOF_NTS_DOMESTIC: ['counselorRemarks'], + HHG_OUTOF_NTS: ['counselorRemarks'], }; // add any additional fields that we also want to always show const neverShow = { HHG_INTO_NTS: ['usesExternalVendor', 'serviceOrderNumber', 'storageFacility', 'requestedDeliveryDate'], - HHG_OUTOF_NTS_DOMESTIC: ['requestedPickupDate'], + HHG_OUTOF_NTS: ['requestedPickupDate'], }; const warnIfMissing = { HHG_INTO_NTS: [{ fieldName: 'tacType' }, { fieldName: 'sacType' }], - HHG_OUTOF_NTS_DOMESTIC: [ + HHG_OUTOF_NTS: [ { fieldName: 'ntsRecordedWeight' }, { fieldName: 'serviceOrderNumber' }, { fieldName: 'tacType' }, @@ -122,7 +122,7 @@ const ServicesCounselingMoveDetails = ({ ], }; const errorIfMissing = { - HHG_OUTOF_NTS_DOMESTIC: [{ fieldName: 'storageFacility' }], + HHG_OUTOF_NTS: [{ fieldName: 'storageFacility' }], PPM: [ { fieldName: 'advanceStatus', @@ -190,7 +190,7 @@ const ServicesCounselingMoveDetails = ({ if (isRetirementOrSeparation) { // destination type must be set for for HHG, NTSR shipments only errorIfMissing.HHG = [{ fieldName: 'destinationType' }]; - errorIfMissing.HHG_OUTOF_NTS_DOMESTIC.push({ fieldName: 'destinationType' }); + errorIfMissing.HHG_OUTOF_NTS.push({ fieldName: 'destinationType' }); } if ( diff --git a/src/pages/Office/ServicesCounselingMoveDetails/ServicesCounselingMoveDetails.test.jsx b/src/pages/Office/ServicesCounselingMoveDetails/ServicesCounselingMoveDetails.test.jsx index 86cd497a0de..8f125ec8ae5 100644 --- a/src/pages/Office/ServicesCounselingMoveDetails/ServicesCounselingMoveDetails.test.jsx +++ b/src/pages/Office/ServicesCounselingMoveDetails/ServicesCounselingMoveDetails.test.jsx @@ -135,7 +135,7 @@ const mtoShipments = [ ]; const ntsrShipmentMissingRequiredInfo = { - shipmentType: 'HHG_OUTOF_NTS_DOMESTIC', + shipmentType: 'HHG_OUTOF_NTS', ntsRecordedWeight: 2000, id: 'ce01a5b8-9b44-8799-8a8d-edb60f2a4aee', serviceOrderNumber: '12341234', diff --git a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx index 9a4b611f0de..5a3d37c59e0 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,27 +8,32 @@ 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'; 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'; 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'; 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'; import { scrollToViewFormikError } from 'utils/validation'; 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); @@ -39,8 +44,10 @@ 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 initialValueOfHasDependents = orders[orderId]?.has_dependents; const orderDocumentId = orders[orderId]?.uploaded_order_id; const amendedOrderDocumentId = orders[orderId]?.uploadedAmendedOrderID || amendedDocumentId; @@ -247,6 +254,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 ; @@ -264,6 +284,10 @@ const ServicesCounselingOrders = ({ files, amendedDocumentId, updateAmendedDocum reportByDate: formatSwaggerDate(values.reportByDate), ordersType: values.ordersType, grade: values.payGrade, + 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 }); }; @@ -292,6 +316,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 (
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, + }), + }), + ); + }); + }); + }); }); diff --git a/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx b/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx index 977a9ebd9ba..44b11a5567d 100644 --- a/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx +++ b/src/pages/Office/ServicesCounselingQueue/ServicesCounselingQueue.jsx @@ -221,6 +221,9 @@ export const counselingColumns = (moveLockFlag, originLocationList, supervisor, { id: 'assignedTo', isFilterable: true, + exportValue: (row) => { + return row.assignedTo ? `${row.assignedTo?.lastName}, ${row.assignedTo?.firstName}` : ''; + }, }, ), ); @@ -415,6 +418,9 @@ export const closeoutColumns = ( { id: 'assignedTo', isFilterable: true, + exportValue: (row) => { + return row.assignedTo ? `${row.assignedTo?.lastName}, ${row.assignedTo?.firstName}` : ''; + }, }, ), ); diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx index ae4b5973cd7..7512e19aa00 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx @@ -382,38 +382,35 @@ describe('PrimeUIShipmentCreateForm', () => { }, ); - it.each(['HHG', 'HHG_INTO_NTS', 'HHG_OUTOF_NTS_DOMESTIC'])( - 'renders the initial form, selecting %s', - async (shipmentType) => { - isBooleanFlagEnabled.mockResolvedValue(false); - expect(await screen.queryByText('BOAT_HAUL_AWAY')).not.toBeInTheDocument(); - expect(await screen.queryByText('BOAT_TOW_AWAY')).not.toBeInTheDocument(); - const shipmentTypeInput = await screen.findByLabelText('Shipment type'); - expect(shipmentTypeInput).toBeInTheDocument(); + it.each(['HHG', 'HHG_INTO_NTS', 'HHG_OUTOF_NTS'])('renders the initial form, selecting %s', async (shipmentType) => { + isBooleanFlagEnabled.mockResolvedValue(false); + expect(await screen.queryByText('BOAT_HAUL_AWAY')).not.toBeInTheDocument(); + expect(await screen.queryByText('BOAT_TOW_AWAY')).not.toBeInTheDocument(); + const shipmentTypeInput = await screen.findByLabelText('Shipment type'); + expect(shipmentTypeInput).toBeInTheDocument(); - // Select the shipment type - await userEvent.selectOptions(shipmentTypeInput, [shipmentType]); + // Select the shipment type + await userEvent.selectOptions(shipmentTypeInput, [shipmentType]); - // Make sure than a PPM-specific field is not visible. - expect(await screen.queryByLabelText('Expected Departure Date')).not.toBeInTheDocument(); + // Make sure than a PPM-specific field is not visible. + expect(await screen.queryByLabelText('Expected Departure Date')).not.toBeInTheDocument(); - expect(await screen.findByText('Shipment Dates')).toBeInTheDocument(); - expect(await screen.findByLabelText('Requested pickup')).toHaveValue(initialValues.requestedPickupDate); + expect(await screen.findByText('Shipment Dates')).toBeInTheDocument(); + expect(await screen.findByLabelText('Requested pickup')).toHaveValue(initialValues.requestedPickupDate); - expect(await screen.findByRole('heading', { name: 'Diversion', level: 2 })).toBeInTheDocument(); - expect(await screen.findByLabelText('Diversion')).not.toBeChecked(); + expect(await screen.findByRole('heading', { name: 'Diversion', level: 2 })).toBeInTheDocument(); + expect(await screen.findByLabelText('Diversion')).not.toBeChecked(); - expect(await screen.findByText('Shipment Weights')).toBeInTheDocument(); - expect(await screen.findByLabelText('Estimated weight (lbs)')).toHaveValue(initialValues.estimatedWeight); + expect(await screen.findByText('Shipment Weights')).toBeInTheDocument(); + expect(await screen.findByLabelText('Estimated weight (lbs)')).toHaveValue(initialValues.estimatedWeight); - expect(await screen.findByText('Shipment Addresses')).toBeInTheDocument(); - expect(await screen.findByText('Pickup Address')).toBeInTheDocument(); - expect(screen.getAllByLabelText('Address 1')[0]).toHaveValue(''); + expect(await screen.findByText('Shipment Addresses')).toBeInTheDocument(); + expect(await screen.findByText('Pickup Address')).toBeInTheDocument(); + expect(screen.getAllByLabelText('Address 1')[0]).toHaveValue(''); - expect(await screen.findByText('Delivery Address')).toBeInTheDocument(); - expect(screen.getAllByLabelText('Address 1')[1]).toHaveValue(''); - }, - ); + expect(await screen.findByText('Delivery Address')).toBeInTheDocument(); + expect(screen.getAllByLabelText('Address 1')[1]).toHaveValue(''); + }); it('renders secondary/tertiary address', async () => { renderShipmentCreateForm(); diff --git a/src/scenes/SystemAdmin/shared/RolesPrivilegesCheckboxes.jsx b/src/scenes/SystemAdmin/shared/RolesPrivilegesCheckboxes.jsx index 1cc554ed70d..d2fdac32c02 100644 --- a/src/scenes/SystemAdmin/shared/RolesPrivilegesCheckboxes.jsx +++ b/src/scenes/SystemAdmin/shared/RolesPrivilegesCheckboxes.jsx @@ -50,6 +50,36 @@ const RolesPrivilegesCheckboxInput = (props) => { input.splice(index, 1); } } + if (input.includes(roleTypes.PRIME_SIMULATOR)) { + index = input.indexOf(roleTypes.PRIME_SIMULATOR); + if (index !== -1) { + input.splice(index, 1); + } + } + if (input.includes(roleTypes.QAE)) { + index = input.indexOf(roleTypes.QAE); + if (index !== -1) { + input.splice(index, 1); + } + } + if (input.includes(roleTypes.CUSTOMER_SERVICE_REPRESENTATIVE)) { + index = input.indexOf(roleTypes.CUSTOMER_SERVICE_REPRESENTATIVE); + if (index !== -1) { + input.splice(index, 1); + } + } + if (input.includes(roleTypes.GSR)) { + index = input.indexOf(roleTypes.GSR); + if (index !== -1) { + input.splice(index, 1); + } + } + if (input.includes(roleTypes.HQ)) { + index = input.indexOf(roleTypes.HQ); + if (index !== -1) { + input.splice(index, 1); + } + } } if (privilegesSelected.includes(elevatedPrivilegeTypes.SAFETY)) { @@ -110,8 +140,16 @@ const RolesPrivilegesCheckboxInput = (props) => { }; const parsePrivilegesCheckboxInput = (input) => { - var index; - if (rolesSelected.includes(roleTypes.CUSTOMER) || rolesSelected.includes(roleTypes.CONTRACTING_OFFICER)) { + let index; + if ( + rolesSelected.includes(roleTypes.CUSTOMER) || + rolesSelected.includes(roleTypes.CONTRACTING_OFFICER) || + rolesSelected.includes(roleTypes.PRIME_SIMULATOR) || + rolesSelected.includes(roleTypes.QAE) || + rolesSelected.includes(roleTypes.CUSTOMER_SERVICE_REPRESENTATIVE) || + rolesSelected.includes(roleTypes.GSR) || + rolesSelected.includes(roleTypes.HQ) + ) { if (input.includes(elevatedPrivilegeTypes.SUPERVISOR)) { index = input.indexOf(elevatedPrivilegeTypes.SUPERVISOR); if (index !== -1) { @@ -140,7 +178,6 @@ const RolesPrivilegesCheckboxInput = (props) => { return privilegesArray; }, []); }; - // filter the privileges to exclude the Safety Moves checkbox if the admin user is NOT a super admin const filteredPrivileges = officeUserPrivileges.filter((privilege) => { if (privilege.privilegeType === elevatedPrivilegeTypes.SAFETY && !adminUser?.super) { @@ -168,7 +205,8 @@ const RolesPrivilegesCheckboxInput = (props) => { optionValue="privilegeType" /> - Privileges cannot be selected with Customer or Contracting Officer roles. + The Supervisor privilege can only be selected for the following roles: Task Ordering Officer, Task Invoicing + Officer, Services Counselor. The Safety Moves privilege can only be selected for the following roles: Task Ordering Officer, Task Invoicing diff --git a/src/shared/constants.js b/src/shared/constants.js index 6f2752d8016..947dd877ba4 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -78,7 +78,7 @@ export const SHIPMENT_OPTIONS = { HHG: 'HHG', PPM: 'PPM', NTS: 'HHG_INTO_NTS', - NTSR: 'HHG_OUTOF_NTS_DOMESTIC', + NTSR: 'HHG_OUTOF_NTS', BOAT: 'BOAT', BOAT_HAUL_AWAY: 'BOAT', BOAT_TOW_AWAY: 'BOAT', @@ -95,7 +95,7 @@ export const SHIPMENT_TYPES = { HHG: 'HHG', PPM: 'PPM', NTS: 'HHG_INTO_NTS', - NTSR: 'HHG_OUTOF_NTS_DOMESTIC', + NTSR: 'HHG_OUTOF_NTS', BOAT_HAUL_AWAY: 'BOAT_HAUL_AWAY', BOAT_TOW_AWAY: 'BOAT_TOW_AWAY', MOBILE_HOME: 'MOBILE_HOME', diff --git a/src/types/address.js b/src/types/address.js index f7152df3fc6..0396874b0cb 100644 --- a/src/types/address.js +++ b/src/types/address.js @@ -43,3 +43,11 @@ export const W2AddressShape = shape({ postalCode: string, usPostRegionCitiesID: string, }); + +export const PortLocationShape = shape({ + portCode: string, + portName: string, + city: string, + state: string, + zip: string, +}); diff --git a/src/utils/formatters.js b/src/utils/formatters.js index 60f06a1f6c3..53f0afa3567 100644 --- a/src/utils/formatters.js +++ b/src/utils/formatters.js @@ -600,3 +600,31 @@ export const constructSCOrderOconusFields = (values) => { null, }; }; + +/** + * @description Converts a string to title case (capitalizes the first letter of each word) + * @param {string} str - The input string to format. + * @returns {string} - the formatted string in the title case. + * */ +export function toTitleCase(str) { + if (!str) return ''; + return str + .toLowerCase() + .split(' ') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +} + +/** + * @description This function is used to format the port in the + * ShipmentAddresses component. + * It displays only the port code, port name, city, state and zip code. + * */ +export function formatPortInfo(port) { + if (port) { + const formattedCity = toTitleCase(port.city); + const formattedState = toTitleCase(port.state); + return `${port.portCode} - ${port.portName}\n${formattedCity}, ${formattedState} ${port.zip}`; + } + return '-'; +} diff --git a/src/utils/formatters.test.js b/src/utils/formatters.test.js index 7a9d8135958..387fae0bef7 100644 --- a/src/utils/formatters.test.js +++ b/src/utils/formatters.test.js @@ -437,3 +437,54 @@ describe('constructSCOrderOconusFields', () => { }); }); }); + +describe('formatPortInfo', () => { + it('formats port information correctly when all fields are provided', () => { + const values = { + portCode: 'PDX', + portName: 'PORTLAND INTL', + city: 'PORTLAND', + state: 'OREGON', + zip: '97220', + }; + const result = formatters.formatPortInfo(values); + expect(result).toEqual('PDX - PORTLAND INTL\nPortland, Oregon 97220'); + }); + + it('returns a dash when no port is provided', () => { + const result = formatters.formatPortInfo(null); + expect(result).toEqual('-'); + }); +}); + +describe('toTitleCase', () => { + it('correctly formats a lowercase string', () => { + const values = 'portland oregon'; + const result = formatters.toTitleCase(values); + expect(result).toEqual('Portland Oregon'); + }); + + it('correctly formats an uppercase string', () => { + const values = 'PORTLAND OREGON'; + const result = formatters.toTitleCase(values); + expect(result).toEqual('Portland Oregon'); + }); + + it('return an empty string when given an empty string', () => { + const values = ''; + const result = formatters.toTitleCase(values); + expect(result).toEqual(''); + }); + + it('return an empty string when given when input is null', () => { + const values = null; + const result = formatters.toTitleCase(values); + expect(result).toEqual(''); + }); + + it('does not alter strings that are already in title case', () => { + const values = 'Portland Oregon'; + const result = formatters.toTitleCase(values); + expect(result).toEqual('Portland Oregon'); + }); +}); diff --git a/swagger-def/definitions/MTOShipment.yaml b/swagger-def/definitions/MTOShipment.yaml index 2e3f55c6fbc..d11a9ec4023 100644 --- a/swagger-def/definitions/MTOShipment.yaml +++ b/swagger-def/definitions/MTOShipment.yaml @@ -217,3 +217,7 @@ properties: - 'i' example: 'd' description: 'Single-letter designator for domestic (d) or international (i) shipments' + podLocation: + $ref: 'Port.yaml' + poeLocation: + $ref: 'Port.yaml' diff --git a/swagger-def/definitions/MTOShipmentType.yaml b/swagger-def/definitions/MTOShipmentType.yaml index c2eb4fce542..f12e559ff60 100644 --- a/swagger-def/definitions/MTOShipmentType.yaml +++ b/swagger-def/definitions/MTOShipmentType.yaml @@ -4,7 +4,7 @@ example: HHG enum: - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - PPM - BOAT_HAUL_AWAY - BOAT_TOW_AWAY @@ -13,7 +13,7 @@ enum: x-display-value: HHG: HHG HHG_INTO_NTS: NTS - HHG_OUTOF_NTS_DOMESTIC: NTS Release + HHG_OUTOF_NTS: NTS Release PPM: PPM BOAT_HAUL_AWAY: Boat Haul-Away BOAT_TOW_AWAY: Boat Tow-Away diff --git a/swagger-def/definitions/ReServiceItem.yaml b/swagger-def/definitions/ReServiceItem.yaml index e8c0b9ab0ed..19e506986a0 100644 --- a/swagger-def/definitions/ReServiceItem.yaml +++ b/swagger-def/definitions/ReServiceItem.yaml @@ -72,7 +72,7 @@ properties: - BOAT_TOW_AWAY - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - MOBILE_HOME - PPM - UNACCOMPANIED_BAGGAGE diff --git a/swagger-def/definitions/prime/MTOShipmentType.yaml b/swagger-def/definitions/prime/MTOShipmentType.yaml index baa8b5ef839..7e75c13feb2 100644 --- a/swagger-def/definitions/prime/MTOShipmentType.yaml +++ b/swagger-def/definitions/prime/MTOShipmentType.yaml @@ -3,7 +3,7 @@ description: | The type of shipment. * `HHG` = Household goods move * `HHG_INTO_NTS` = HHG into Non-temporary storage (NTS) - * `HHG_OUTOF_NTS_DOMESTIC` = HHG out of Non-temporary storage (NTS Release) + * `HHG_OUTOF_NTS` = HHG out of Non-temporary storage (NTS Release) * `PPM` = Personally Procured Move also known as Do It Yourself (DITY) * `BOAT_HAUL_AWAY` = Boat shipment that requires additional equipment to haul it to it's destination * `BOAT_TOW_AWAY` = Boat shipment that has a road-worthy trailer @@ -15,14 +15,14 @@ enum: - BOAT_TOW_AWAY - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - MOBILE_HOME - PPM - UNACCOMPANIED_BAGGAGE x-display-value: HHG: Household goods move (HHG) HHG_INTO_NTS: HHG into Non-temporary storage (NTS) - HHG_OUTOF_NTS_DOMESTIC: HHG out of Non-temporary storage (NTS Release) + HHG_OUTOF_NTS: HHG out of Non-temporary storage (NTS Release) PPM: Personally Procured Move also known as Do It Yourself (DITY) BOAT_HAUL_AWAY: Boat shipment that requires additional equipment to haul it to it's destination BOAT_TOW_AWAY: Boat shipment that has a road-worthy trailer diff --git a/swagger-def/definitions/prime/v2/MTOShipmentType.yaml b/swagger-def/definitions/prime/v2/MTOShipmentType.yaml index baa8b5ef839..7e75c13feb2 100644 --- a/swagger-def/definitions/prime/v2/MTOShipmentType.yaml +++ b/swagger-def/definitions/prime/v2/MTOShipmentType.yaml @@ -3,7 +3,7 @@ description: | The type of shipment. * `HHG` = Household goods move * `HHG_INTO_NTS` = HHG into Non-temporary storage (NTS) - * `HHG_OUTOF_NTS_DOMESTIC` = HHG out of Non-temporary storage (NTS Release) + * `HHG_OUTOF_NTS` = HHG out of Non-temporary storage (NTS Release) * `PPM` = Personally Procured Move also known as Do It Yourself (DITY) * `BOAT_HAUL_AWAY` = Boat shipment that requires additional equipment to haul it to it's destination * `BOAT_TOW_AWAY` = Boat shipment that has a road-worthy trailer @@ -15,14 +15,14 @@ enum: - BOAT_TOW_AWAY - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - MOBILE_HOME - PPM - UNACCOMPANIED_BAGGAGE x-display-value: HHG: Household goods move (HHG) HHG_INTO_NTS: HHG into Non-temporary storage (NTS) - HHG_OUTOF_NTS_DOMESTIC: HHG out of Non-temporary storage (NTS Release) + HHG_OUTOF_NTS: HHG out of Non-temporary storage (NTS Release) PPM: Personally Procured Move also known as Do It Yourself (DITY) BOAT_HAUL_AWAY: Boat shipment that requires additional equipment to haul it to it's destination BOAT_TOW_AWAY: Boat shipment that has a road-worthy trailer diff --git a/swagger-def/definitions/prime/v3/MTOShipmentType.yaml b/swagger-def/definitions/prime/v3/MTOShipmentType.yaml index baa8b5ef839..7e75c13feb2 100644 --- a/swagger-def/definitions/prime/v3/MTOShipmentType.yaml +++ b/swagger-def/definitions/prime/v3/MTOShipmentType.yaml @@ -3,7 +3,7 @@ description: | The type of shipment. * `HHG` = Household goods move * `HHG_INTO_NTS` = HHG into Non-temporary storage (NTS) - * `HHG_OUTOF_NTS_DOMESTIC` = HHG out of Non-temporary storage (NTS Release) + * `HHG_OUTOF_NTS` = HHG out of Non-temporary storage (NTS Release) * `PPM` = Personally Procured Move also known as Do It Yourself (DITY) * `BOAT_HAUL_AWAY` = Boat shipment that requires additional equipment to haul it to it's destination * `BOAT_TOW_AWAY` = Boat shipment that has a road-worthy trailer @@ -15,14 +15,14 @@ enum: - BOAT_TOW_AWAY - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - MOBILE_HOME - PPM - UNACCOMPANIED_BAGGAGE x-display-value: HHG: Household goods move (HHG) HHG_INTO_NTS: HHG into Non-temporary storage (NTS) - HHG_OUTOF_NTS_DOMESTIC: HHG out of Non-temporary storage (NTS Release) + HHG_OUTOF_NTS: HHG out of Non-temporary storage (NTS Release) PPM: Personally Procured Move also known as Do It Yourself (DITY) BOAT_HAUL_AWAY: Boat shipment that requires additional equipment to haul it to it's destination BOAT_TOW_AWAY: Boat shipment that has a road-worthy trailer diff --git a/swagger-def/ghc.yaml b/swagger-def/ghc.yaml index 29b0e5c7fdb..265790c7a92 100644 --- a/swagger-def/ghc.yaml +++ b/swagger-def/ghc.yaml @@ -1062,6 +1062,7 @@ paths: * Releasing / Receiving agents * Actual Pro Gear Weight * Actual Spouse Pro Gear Weight + * Location of the POE/POD consumes: - application/json produces: @@ -5752,6 +5753,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-def/internal.yaml b/swagger-def/internal.yaml index 0a9486e0c83..d4048c289ce 100644 --- a/swagger-def/internal.yaml +++ b/swagger-def/internal.yaml @@ -1807,7 +1807,7 @@ definitions: enum: - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - PPM - BOAT_HAUL_AWAY - BOAT_TOW_AWAY @@ -1816,7 +1816,7 @@ definitions: x-display-value: HHG: HHG HHG_INTO_NTS: NTS - HHG_OUTOF_NTS_DOMESTIC: NTS Release + HHG_OUTOF_NTS: NTS Release PPM: PPM BOAT_HAUL_AWAY: Boat Haul-Away BOAT_TOW_AWAY: Boat Tow-Away diff --git a/swagger-def/prime_v2.yaml b/swagger-def/prime_v2.yaml index e0739b1151d..bf11d799eb1 100644 --- a/swagger-def/prime_v2.yaml +++ b/swagger-def/prime_v2.yaml @@ -136,7 +136,7 @@ paths: value: { 'moveTaskOrderId': '5691c951-c35c-49a8-a1d5-a4b7ea7b7ad8', - 'shipmentType': 'HHG_OUTOF_NTS_DOMESTIC', + 'shipmentType': 'HHG_OUTOF_NTS', 'agents': [ { diff --git a/swagger-def/prime_v3.yaml b/swagger-def/prime_v3.yaml index 5bce4298a3f..66a497501f4 100644 --- a/swagger-def/prime_v3.yaml +++ b/swagger-def/prime_v3.yaml @@ -128,7 +128,7 @@ paths: value: { 'moveTaskOrderId': '5691c951-c35c-49a8-a1d5-a4b7ea7b7ad8', - 'shipmentType': 'HHG_OUTOF_NTS_DOMESTIC', + 'shipmentType': 'HHG_OUTOF_NTS', 'agents': [ { diff --git a/swagger-def/support.yaml b/swagger-def/support.yaml index c11afc65054..4b230febd84 100644 --- a/swagger-def/support.yaml +++ b/swagger-def/support.yaml @@ -1493,7 +1493,7 @@ definitions: enum: - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - PPM - BOAT_HAUL_AWAY - BOAT_TOW_AWAY @@ -1502,7 +1502,7 @@ definitions: x-display-value: HHG: HHG HHG_INTO_NTS: NTS - HHG_OUTOF_NTS_DOMESTIC: NTS Release + HHG_OUTOF_NTS: NTS Release PPM: PPM BOAT_HAUL_AWAY: Boat Haul-Away BOAT_TOW_AWAY: Boat Tow-Away diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml index 945523f20de..99c32d38083 100644 --- a/swagger/ghc.yaml +++ b/swagger/ghc.yaml @@ -1113,6 +1113,7 @@ paths: * Releasing / Receiving agents * Actual Pro Gear Weight * Actual Spouse Pro Gear Weight + * Location of the POE/POD consumes: - application/json produces: @@ -6020,6 +6021,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 @@ -8951,7 +8956,7 @@ definitions: enum: - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - PPM - BOAT_HAUL_AWAY - BOAT_TOW_AWAY @@ -8960,7 +8965,7 @@ definitions: x-display-value: HHG: HHG HHG_INTO_NTS: NTS - HHG_OUTOF_NTS_DOMESTIC: NTS Release + HHG_OUTOF_NTS: NTS Release PPM: PPM BOAT_HAUL_AWAY: Boat Haul-Away BOAT_TOW_AWAY: Boat Tow-Away @@ -10508,6 +10513,102 @@ definitions: - originalAddress - newAddress - contractorRemarks + Port: + description: A port that is used to move an international shipment. + type: object + properties: + id: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 + portType: + type: string + description: Port type A (Air), B (Border Crossing), S (Sea) + enum: + - A + - B + - S + portCode: + type: string + description: 3 or 4 digit port code + example: '0431' + portName: + type: string + description: Name of the port + example: PORTLAND INTL + city: + type: string + example: PORTLAND + county: + type: string + example: MULTNOMAH + state: + type: string + description: US state + example: OR + enum: + - AL + - AK + - AR + - AZ + - CA + - CO + - CT + - DC + - DE + - FL + - GA + - HI + - IA + - ID + - IL + - IN + - KS + - KY + - LA + - MA + - MD + - ME + - MI + - MN + - MO + - MS + - MT + - NC + - ND + - NE + - NH + - NJ + - NM + - NV + - NY + - OH + - OK + - OR + - PA + - RI + - SC + - SD + - TN + - TX + - UT + - VA + - VT + - WA + - WI + - WV + - WY + zip: + type: string + format: zip + title: ZIP + example: '99501' + pattern: ^(\d{5}([\-]\d{4})?)$ + country: + type: string + example: US + pattern: ^[A-Z]{2}$ + description: Two-letter country code MTOShipment: properties: moveTaskOrderID: @@ -10738,6 +10839,10 @@ definitions: description: >- Single-letter designator for domestic (d) or international (i) shipments + podLocation: + $ref: '#/definitions/Port' + poeLocation: + $ref: '#/definitions/Port' LOATypeNullable: description: The Line of accounting (TAC/SAC) type that will be used for the shipment type: string @@ -11806,7 +11911,7 @@ definitions: - BOAT_TOW_AWAY - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - MOBILE_HOME - PPM - UNACCOMPANIED_BAGGAGE diff --git a/swagger/internal.yaml b/swagger/internal.yaml index 99da9030213..675e89ef785 100644 --- a/swagger/internal.yaml +++ b/swagger/internal.yaml @@ -1850,7 +1850,7 @@ definitions: enum: - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - PPM - BOAT_HAUL_AWAY - BOAT_TOW_AWAY @@ -1859,7 +1859,7 @@ definitions: x-display-value: HHG: HHG HHG_INTO_NTS: NTS - HHG_OUTOF_NTS_DOMESTIC: NTS Release + HHG_OUTOF_NTS: NTS Release PPM: PPM BOAT_HAUL_AWAY: Boat Haul-Away BOAT_TOW_AWAY: Boat Tow-Away diff --git a/swagger/prime.yaml b/swagger/prime.yaml index 100c889a9b4..b27503aba19 100644 --- a/swagger/prime.yaml +++ b/swagger/prime.yaml @@ -3966,7 +3966,7 @@ definitions: The type of shipment. * `HHG` = Household goods move * `HHG_INTO_NTS` = HHG into Non-temporary storage (NTS) - * `HHG_OUTOF_NTS_DOMESTIC` = HHG out of Non-temporary storage (NTS Release) + * `HHG_OUTOF_NTS` = HHG out of Non-temporary storage (NTS Release) * `PPM` = Personally Procured Move also known as Do It Yourself (DITY) * `BOAT_HAUL_AWAY` = Boat shipment that requires additional equipment to haul it to it's destination * `BOAT_TOW_AWAY` = Boat shipment that has a road-worthy trailer @@ -3978,14 +3978,14 @@ definitions: - BOAT_TOW_AWAY - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - MOBILE_HOME - PPM - UNACCOMPANIED_BAGGAGE x-display-value: HHG: Household goods move (HHG) HHG_INTO_NTS: HHG into Non-temporary storage (NTS) - HHG_OUTOF_NTS_DOMESTIC: HHG out of Non-temporary storage (NTS Release) + HHG_OUTOF_NTS: HHG out of Non-temporary storage (NTS Release) PPM: Personally Procured Move also known as Do It Yourself (DITY) BOAT_HAUL_AWAY: >- Boat shipment that requires additional equipment to haul it to it's diff --git a/swagger/prime_v2.yaml b/swagger/prime_v2.yaml index f8f79ca6ce4..c482b1ea7e9 100644 --- a/swagger/prime_v2.yaml +++ b/swagger/prime_v2.yaml @@ -187,7 +187,7 @@ paths: summary: NTS Release value: moveTaskOrderId: 5691c951-c35c-49a8-a1d5-a4b7ea7b7ad8 - shipmentType: HHG_OUTOF_NTS_DOMESTIC + shipmentType: HHG_OUTOF_NTS agents: - firstName: Edgar lastName: Taylor @@ -2370,7 +2370,7 @@ definitions: The type of shipment. * `HHG` = Household goods move * `HHG_INTO_NTS` = HHG into Non-temporary storage (NTS) - * `HHG_OUTOF_NTS_DOMESTIC` = HHG out of Non-temporary storage (NTS Release) + * `HHG_OUTOF_NTS` = HHG out of Non-temporary storage (NTS Release) * `PPM` = Personally Procured Move also known as Do It Yourself (DITY) * `BOAT_HAUL_AWAY` = Boat shipment that requires additional equipment to haul it to it's destination * `BOAT_TOW_AWAY` = Boat shipment that has a road-worthy trailer @@ -2382,14 +2382,14 @@ definitions: - BOAT_TOW_AWAY - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - MOBILE_HOME - PPM - UNACCOMPANIED_BAGGAGE x-display-value: HHG: Household goods move (HHG) HHG_INTO_NTS: HHG into Non-temporary storage (NTS) - HHG_OUTOF_NTS_DOMESTIC: HHG out of Non-temporary storage (NTS Release) + HHG_OUTOF_NTS: HHG out of Non-temporary storage (NTS Release) PPM: Personally Procured Move also known as Do It Yourself (DITY) BOAT_HAUL_AWAY: >- Boat shipment that requires additional equipment to haul it to it's diff --git a/swagger/prime_v3.yaml b/swagger/prime_v3.yaml index d5abe7d4ade..5e6d3df8cf2 100644 --- a/swagger/prime_v3.yaml +++ b/swagger/prime_v3.yaml @@ -169,7 +169,7 @@ paths: summary: NTS Release value: moveTaskOrderId: 5691c951-c35c-49a8-a1d5-a4b7ea7b7ad8 - shipmentType: HHG_OUTOF_NTS_DOMESTIC + shipmentType: HHG_OUTOF_NTS agents: - firstName: Edgar lastName: Taylor @@ -2477,7 +2477,7 @@ definitions: The type of shipment. * `HHG` = Household goods move * `HHG_INTO_NTS` = HHG into Non-temporary storage (NTS) - * `HHG_OUTOF_NTS_DOMESTIC` = HHG out of Non-temporary storage (NTS Release) + * `HHG_OUTOF_NTS` = HHG out of Non-temporary storage (NTS Release) * `PPM` = Personally Procured Move also known as Do It Yourself (DITY) * `BOAT_HAUL_AWAY` = Boat shipment that requires additional equipment to haul it to it's destination * `BOAT_TOW_AWAY` = Boat shipment that has a road-worthy trailer @@ -2489,14 +2489,14 @@ definitions: - BOAT_TOW_AWAY - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - MOBILE_HOME - PPM - UNACCOMPANIED_BAGGAGE x-display-value: HHG: Household goods move (HHG) HHG_INTO_NTS: HHG into Non-temporary storage (NTS) - HHG_OUTOF_NTS_DOMESTIC: HHG out of Non-temporary storage (NTS Release) + HHG_OUTOF_NTS: HHG out of Non-temporary storage (NTS Release) PPM: Personally Procured Move also known as Do It Yourself (DITY) BOAT_HAUL_AWAY: >- Boat shipment that requires additional equipment to haul it to it's diff --git a/swagger/support.yaml b/swagger/support.yaml index 6895258916d..26b02e68d32 100644 --- a/swagger/support.yaml +++ b/swagger/support.yaml @@ -1608,7 +1608,7 @@ definitions: enum: - HHG - HHG_INTO_NTS - - HHG_OUTOF_NTS_DOMESTIC + - HHG_OUTOF_NTS - PPM - BOAT_HAUL_AWAY - BOAT_TOW_AWAY @@ -1617,7 +1617,7 @@ definitions: x-display-value: HHG: HHG HHG_INTO_NTS: NTS - HHG_OUTOF_NTS_DOMESTIC: NTS Release + HHG_OUTOF_NTS: NTS Release PPM: PPM BOAT_HAUL_AWAY: Boat Haul-Away BOAT_TOW_AWAY: Boat Tow-Away