diff --git a/pkg/handlers/internalapi/internal/payloads/model_to_payload.go b/pkg/handlers/internalapi/internal/payloads/model_to_payload.go index 464d36267e9..f0b5fd06ed3 100644 --- a/pkg/handlers/internalapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/internalapi/internal/payloads/model_to_payload.go @@ -104,6 +104,7 @@ func PPMShipment(storer storage.FileStorer, ppmShipment *models.PPMShipment) *in AdvanceAmountRequested: handlers.FmtCost(ppmShipment.AdvanceAmountRequested), HasReceivedAdvance: ppmShipment.HasReceivedAdvance, AdvanceAmountReceived: handlers.FmtCost(ppmShipment.AdvanceAmountReceived), + AdvanceStatus: (*internalmessages.PPMAdvanceStatus)(ppmShipment.AdvanceStatus), WeightTickets: WeightTickets(storer, ppmShipment.WeightTickets), MovingExpenses: MovingExpenses(storer, ppmShipment.MovingExpenses), ProGearWeightTickets: ProGearWeightTickets(storer, ppmShipment.ProgearWeightTickets), diff --git a/playwright/tests/my/milmove/ppms/customerPpmTestFixture.js b/playwright/tests/my/milmove/ppms/customerPpmTestFixture.js index 35f5d59400c..392ea75d374 100644 --- a/playwright/tests/my/milmove/ppms/customerPpmTestFixture.js +++ b/playwright/tests/my/milmove/ppms/customerPpmTestFixture.js @@ -908,7 +908,12 @@ export class CustomerPpmPage extends CustomerPage { await expect(this.page.locator('.usa-alert--success')).toContainText('You submitted documentation for review.'); - const stepContainer = this.page.locator('[data-testid="stepContainer5"]'); + let stepContainer = this.page.locator('[data-testid="stepContainer6"]'); + + if (stepContainer == null) { + stepContainer = this.page.locator('[data-testid="stepContainer5"]'); + } + await expect(stepContainer.getByRole('button', { name: 'Download Incentive Packet' })).toBeDisabled(); await expect(stepContainer.getByText(/PPM documentation submitted: \d{2} \w{3} \d{4}/)).toBeVisible(); } diff --git a/playwright/tests/my/milmove/ppms/entireShipmentOnboarding.spec.js b/playwright/tests/my/milmove/ppms/entireShipmentOnboarding.spec.js index d330106d3c7..ea57a703ae2 100644 --- a/playwright/tests/my/milmove/ppms/entireShipmentOnboarding.spec.js +++ b/playwright/tests/my/milmove/ppms/entireShipmentOnboarding.spec.js @@ -45,11 +45,16 @@ class CustomerPpmOnboardingPage extends CustomerPpmPage { /** */ - async verifyStep5ExistsAndBtnIsDisabled() { - const stepContainer5 = this.page.locator('[data-testid="stepContainer5"]'); - await expect(stepContainer5.getByRole('button', { name: 'Upload PPM Documents' })).toBeDisabled(); + async verifyManagePPMStepExistsAndBtnIsDisabled() { + const stepContainer = this.page.locator('[data-testid="stepContainer6"]'); + + if (stepContainer == null) { + this.page.locator('[data-testid="stepContainer5"]'); + } + + await expect(stepContainer.getByRole('button', { name: 'Upload PPM Documents' })).toBeDisabled(); await expect( - stepContainer5.locator('p').getByText('After a counselor approves your PPM, you will be able to:'), + stepContainer.locator('p').getByText('After a counselor approves your PPM, you will be able to:'), ).toBeVisible(); } @@ -119,7 +124,7 @@ test.describe('Entire PPM onboarding flow', () => { await customerPpmOnboardingPage.submitsAdvancePage({ addAdvance: true, isMobile }); await customerPpmOnboardingPage.navigateToAgreementAndSign(); await customerPpmOnboardingPage.submitMove(); - await customerPpmOnboardingPage.verifyStep5ExistsAndBtnIsDisabled(); + await customerPpmOnboardingPage.verifyManagePPMStepExistsAndBtnIsDisabled(); }); test('happy path with edits and backs', async () => { @@ -138,7 +143,7 @@ test.describe('Entire PPM onboarding flow', () => { await customerPpmOnboardingPage.navigateToAgreementAndSign(); await customerPpmOnboardingPage.submitMove(); - await customerPpmOnboardingPage.verifyStep5ExistsAndBtnIsDisabled(); + await customerPpmOnboardingPage.verifyManagePPMStepExistsAndBtnIsDisabled(); }); }); }); diff --git a/playwright/tests/my/milmove/ppms/navigateToUploadDocs.spec.js b/playwright/tests/my/milmove/ppms/navigateToUploadDocs.spec.js index bc12f20a022..a40b8dfe36d 100644 --- a/playwright/tests/my/milmove/ppms/navigateToUploadDocs.spec.js +++ b/playwright/tests/my/milmove/ppms/navigateToUploadDocs.spec.js @@ -16,9 +16,14 @@ test.describe('PPM Request Payment - Begin providing documents flow', () => { test('has upload documents button enabled', async ({ page }) => { await expect(page.getByRole('heading', { name: 'Your move is in progress.' })).toBeVisible(); - const stepContainer5 = page.getByTestId('stepContainer5'); - await expect(stepContainer5.locator('p').getByText('15 Apr 2022')).toBeVisible(); - await stepContainer5.getByRole('button', { name: 'Upload PPM Documents' }).click(); + let stepContainer = page.getByTestId('stepContainer6'); + + if (stepContainer == null) { + stepContainer = page.getByTestId('stepContainer5'); + } + + await expect(stepContainer.locator('p').getByText('15 Apr 2022')).toBeVisible(); + await stepContainer.getByRole('button', { name: 'Upload PPM Documents' }).click(); await expect(page).toHaveURL(/\/moves\/[^/]+\/shipments\/[^/]+\/about/); }); }); diff --git a/src/pages/MyMove/Home/index.jsx b/src/pages/MyMove/Home/index.jsx index 293c1ce9db6..f89ab76078a 100644 --- a/src/pages/MyMove/Home/index.jsx +++ b/src/pages/MyMove/Home/index.jsx @@ -54,6 +54,7 @@ import { formatCustomerDate, formatWeight } from 'utils/formatters'; import { isPPMAboutInfoComplete, isPPMShipmentComplete, isWeightTicketComplete } from 'utils/shipments'; import withRouter from 'utils/routing'; import { RouterShape } from 'types/router'; +import { ADVANCE_STATUSES } from 'constants/ppms'; const Description = ({ className, children, dataTestId }) => (

@@ -141,6 +142,30 @@ export class Home extends Component { return mtoShipments?.filter((s) => s.shipmentType === SHIPMENT_OPTIONS.PPM)?.every((s) => isPPMShipmentComplete(s)); } + get hasAdvanceApproved() { + const { mtoShipments } = this.props; + // determine if at least one advance was APPROVED (advance_status in ppm_shipments table is not nil) + const appovedAdvances = mtoShipments.filter( + (shipment) => shipment?.ppmShipment?.advanceStatus === ADVANCE_STATUSES.APPROVED.apiValue, + ); + return !!appovedAdvances.length; + } + + get hasAllAdvancesRejected() { + // check to see if all advance_status are REJECTED + const { mtoShipments } = this.props; + const rejectedAdvances = mtoShipments.filter( + (shipment) => shipment?.ppmShipment?.advanceStatus === ADVANCE_STATUSES.REJECTED.apiValue, + ); + return !this.hasAdvanceApproved && rejectedAdvances.length > 0; + } + + get hasAdvanceRequested() { + const { mtoShipments } = this.props; + const requestedAdvances = mtoShipments.filter((shipment) => shipment?.ppmShipment?.hasRequestedAdvance); + return !!requestedAdvances.length; + } + get isMoveApproved() { const { move } = this.props; return move.status === MOVE_STATUSES.APPROVED; @@ -384,6 +409,7 @@ export class Home extends Component { // eslint-disable-next-line camelcase const currentLocation = current_location; + const shipmentNumbersByType = {}; return ( <> @@ -535,8 +561,88 @@ export class Home extends Component { )} + {!!ppmShipments.length && this.hasSubmittedMove && this.hasAdvanceRequested && ( + + + {this.hasAdvanceApproved && ( + <> + + Your Advance Operating Allowance (AOA) request has been reviewed. Download the paperwork + for approved requests and submit it to your Finance Office to receive your advance. +
+
The amount you receive will be deducted from your PPM incentive payment. If your + incentive ends up being less than your advance, you will be required to pay back the + difference. +
+
+
+ {ppmShipments.map((shipment) => { + const { shipmentType } = shipment; + if (shipmentNumbersByType[shipmentType]) { + shipmentNumbersByType[shipmentType] += 1; + } else { + shipmentNumbersByType[shipmentType] = 1; + } + const shipmentNumber = shipmentNumbersByType[shipmentType]; + return ( + <> + + {shipmentTypes[shipment.shipmentType]} + {` ${shipmentNumber} `} + + {shipment?.ppmShipment?.advanceStatus === ADVANCE_STATUSES.APPROVED.apiValue && ( + // TODO: B-18060 will add link to method that will create the AOA packet and return for download +

+ + Download AOA Paperwork (PDF) + +

+ )} + {shipment?.ppmShipment?.advanceStatus === ADVANCE_STATUSES.REJECTED.apiValue && ( + Advance request denied + )} + {shipment?.ppmShipment?.advanceStatus == null && ( + Advance request pending + )} + + ); + })} + + )} + {this.hasAllAdvancesRejected && ( + + Your Advance Operating Allowance (AOA) request has been denied. You may be able to use your + Government Travel Charge Card (GTCC). Contact your local transportation office to verify + GTCC usage authorization or ask any questions. + + )} + {!this.hasAdvanceApproved && !this.hasAllAdvancesRejected && ( + + Your service will review your request for an Advance Operating Allowance (AOA). If approved, + you will be able to download the paperwork for your request and submit it to your Finance + Office to receive your advance. +
+
The amount you receive will be deducted from your PPM incentive payment. If your + incentive ends up being less than your advance, you will be required to pay back the + difference. +
+ )} + + + )} {!!ppmShipments.length && this.hasSubmittedMove && ( - + )} diff --git a/src/pages/MyMove/Home/index.test.jsx b/src/pages/MyMove/Home/index.test.jsx index 1201d8d6c3d..10faf81d6b1 100644 --- a/src/pages/MyMove/Home/index.test.jsx +++ b/src/pages/MyMove/Home/index.test.jsx @@ -194,6 +194,40 @@ const ppmShipmentWithCompleteWeightTicket = { }, }; +const approvedAdvancePPMShipment = { + ...incompletePPMShipment, + ppmShipment: { + ...incompletePPMShipment.ppmShipment, + sitExpected: false, + estimatedWeight: 4000, + hasProGear: false, + estimatedIncentive: 10000000, + hasRequestedAdvance: true, + advanceAmountRequested: 30000, + advanceStatus: 'APPROVED', + status: ppmShipmentStatuses.SUBMITTED, + updatedAt: ppmShipmentUpdatedDate.toISOString(), + eTag: window.btoa(ppmShipmentUpdatedDate.toISOString()), + }, +}; + +const rejectedAdvancePPMShipment = { + ...incompletePPMShipment, + ppmShipment: { + ...incompletePPMShipment.ppmShipment, + sitExpected: false, + estimatedWeight: 4000, + hasProGear: false, + estimatedIncentive: 10000000, + hasRequestedAdvance: true, + advanceAmountRequested: 30000, + advanceStatus: 'REJECTED', + status: ppmShipmentStatuses.SUBMITTED, + updatedAt: ppmShipmentUpdatedDate.toISOString(), + eTag: window.btoa(ppmShipmentUpdatedDate.toISOString()), + }, +}; + const mountHomeWithProviders = (props = {}) => { return mount( @@ -510,7 +544,7 @@ describe('Home component', () => { expect(ordersStep.prop('editBtnLabel')).toEqual('Upload documents'); }); - it('renders Step 5', () => { + it('renders Manage your PPM Step', () => { render(); expect(screen.getByText('Manage your PPM')).toBeInTheDocument(); }); @@ -521,6 +555,65 @@ describe('Home component', () => { }); }); + describe('for advance request approved PPM', () => { + it('renders advance request submitted for PPM', () => { + const mtoShipments = [submittedPPMShipment]; + const props = { ...defaultProps, ...propUpdates, mtoShipments }; + render(); + expect(screen.getByText('Advance request submitted')).toBeInTheDocument(); + }); + + it('renders advance request submitted for PPM', () => { + const mtoShipments = [approvedAdvancePPMShipment]; + const props = { ...defaultProps, ...propUpdates, mtoShipments }; + render(); + expect(screen.getByText('Download AOA Paperwork (PDF)')).toBeInTheDocument(); + }); + + it('renders advance request reviewed with 1 approved PPM', () => { + const mtoShipments = [approvedAdvancePPMShipment]; + const wrapper = mountHomeWithProviders({ ...propUpdates, mtoShipments }); + const advanceStep = wrapper.find('Step[step="5"]'); + expect(advanceStep.prop('completedHeaderText')).toEqual('Advance request reviewed'); + + const props = { ...defaultProps, ...propUpdates, mtoShipments }; + render(); + expect(screen.getByText('Download AOA Paperwork (PDF)')).toBeInTheDocument(); + }); + + it('renders advance request reviewed for approved advance for PPM with HHG', () => { + const mtoShipments = [{ id: v4(), shipmentType: SHIPMENT_OPTIONS.HHG }, approvedAdvancePPMShipment]; + const wrapper = mountHomeWithProviders({ ...propUpdates, mtoShipments }); + const advanceStep = wrapper.find('Step[step="5"]'); + expect(advanceStep.prop('completedHeaderText')).toEqual('Advance request reviewed'); + + const props = { ...defaultProps, ...propUpdates, mtoShipments }; + render(); + expect(screen.getByText('Download AOA Paperwork (PDF)')).toBeInTheDocument(); + }); + + it('renders advance request reviewed with 1 approved and 1 rejected advance', () => { + const mtoShipments = [approvedAdvancePPMShipment, rejectedAdvancePPMShipment]; + const wrapper = mountHomeWithProviders({ ...propUpdates, mtoShipments }); + const advanceStep = wrapper.find('Step[step="5"]'); + + expect(advanceStep.prop('completedHeaderText')).toEqual('Advance request reviewed'); + + const props = { ...defaultProps, ...propUpdates, mtoShipments }; + render(); + expect(screen.getByText('Download AOA Paperwork (PDF)')).toBeInTheDocument(); + expect(screen.getByText('Advance request denied')).toBeInTheDocument(); + }); + + it('renders advance request denied for PPM', () => { + const mtoShipments = [rejectedAdvancePPMShipment]; + const wrapper = mountHomeWithProviders({ ...propUpdates, mtoShipments }); + const advanceStep = wrapper.find('Step[step="5"]'); + + expect(advanceStep.prop('completedHeaderText')).toEqual('Advance request denied'); + }); + }); + describe('for HHG moves (no PPM)', () => { const mtoShipments = [{ id: v4(), shipmentType: SHIPMENT_OPTIONS.HHG }]; @@ -542,7 +635,7 @@ describe('Home component', () => { expect(ordersStep.prop('editBtnLabel')).toEqual('Upload documents'); }); - it('does not render Step 5', () => { + it('does not render Manage your PPM Step', () => { render(); expect(screen.queryByText('Manage your PPM')).not.toBeInTheDocument(); }); @@ -574,7 +667,7 @@ describe('Home component', () => { expect(ordersStep.prop('editBtnLabel')).toEqual('Upload documents'); }); - it('does not render Step 5', () => { + it('does not render Manage your PPM Step', () => { render(); expect(screen.queryByText('Manage your PPM')).not.toBeInTheDocument(); }); @@ -618,7 +711,7 @@ describe('Home component', () => { expect(ordersStep.prop('editBtnLabel')).toEqual('Upload documents'); }); - it('renders Step 5', () => { + it('renders Manage your PPM Step', () => { render(); expect(screen.getByText('Manage your PPM')).toBeInTheDocument(); });