@defra-fish/gafl-webapp-service 1.65.0-rc.2 → 1.65.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra-fish/gafl-webapp-service",
3
- "version": "1.65.0-rc.2",
3
+ "version": "1.65.0-rc.4",
4
4
  "description": "The websales frontend for the GAFL service",
5
5
  "type": "module",
6
6
  "engines": {
@@ -36,8 +36,8 @@
36
36
  "prepare": "gulp --gulpfile build/gulpfile.cjs"
37
37
  },
38
38
  "dependencies": {
39
- "@defra-fish/business-rules-lib": "1.65.0-rc.2",
40
- "@defra-fish/connectors-lib": "1.65.0-rc.2",
39
+ "@defra-fish/business-rules-lib": "1.65.0-rc.4",
40
+ "@defra-fish/connectors-lib": "1.65.0-rc.4",
41
41
  "@defra/hapi-gapi": "2.0.0",
42
42
  "@hapi/boom": "9.1.2",
43
43
  "@hapi/catbox-redis": "6.0.2",
@@ -79,5 +79,5 @@
79
79
  "./gafl-jest-matchers.js"
80
80
  ]
81
81
  },
82
- "gitHead": "2f8e3ec17ed29caf89f585324d8dd4b725ff4586"
82
+ "gitHead": "47a57b982b698bea3a6e03473d7bbaa4ec733180"
83
83
  }
@@ -1,5 +1,5 @@
1
1
  import handler from '../cancel-recurring-payment-authentication-handler'
2
- import { CANCEL_RP_IDENTIFY, CANCEL_RP_DETAILS, CANCEL_RP_AGREEMENT_NOT_FOUND } from '../../uri.js'
2
+ import { CANCEL_RP_IDENTIFY, CANCEL_RP_DETAILS, CANCEL_RP_AGREEMENT_NOT_FOUND, CANCEL_RP_ALREADY_CANCELLED } from '../../uri.js'
3
3
  import { salesApi } from '@defra-fish/connectors-lib'
4
4
 
5
5
  jest.mock('../../processors/uri-helper.js')
@@ -19,7 +19,8 @@ jest.mock('@defra-fish/business-rules-lib', () => ({
19
19
  jest.mock('../../uri.js', () => ({
20
20
  CANCEL_RP_IDENTIFY: { page: 'cancel-rp-identify page', uri: Symbol('cancel-rp-identify-uri') },
21
21
  CANCEL_RP_DETAILS: { uri: Symbol('cancel-rp-details-uri') },
22
- CANCEL_RP_AGREEMENT_NOT_FOUND: { uri: Symbol('cancel-rp-agreement-not-found-uri') }
22
+ CANCEL_RP_AGREEMENT_NOT_FOUND: { uri: Symbol('cancel-rp-agreement-not-found-uri') },
23
+ CANCEL_RP_ALREADY_CANCELLED: { uri: Symbol('cancel-rp-already-cancelled-uri ') }
23
24
  }))
24
25
  jest.mock('../../processors/recurring-payments-write-cache.js')
25
26
 
@@ -165,12 +166,12 @@ describe('Cancel RP Authentication Handler', () => {
165
166
  })
166
167
  })
167
168
 
168
- describe('Unsuccessful authentication - RCP cancelled', () => {
169
- it('redirects to the CANCEL_RP_IDENTIFY.uri', async () => {
169
+ describe('Unsuccessful authentication - RCP already cancelled', () => {
170
+ it('redirects to the CANCEL_RP_ALREADY_CANCELLED.uri', async () => {
170
171
  const { h } = await invokeHandlerWithMocks({
171
172
  salesApiResponse: { permission: { id: 'perm-id' }, recurringPayment: { id: 'rcp-id', status: 1, cancelledDate: '2024-01-01' } }
172
173
  })
173
- expect(h.redirectWithLanguageCode).toHaveBeenCalledWith(CANCEL_RP_IDENTIFY.uri)
174
+ expect(h.redirectWithLanguageCode).toHaveBeenCalledWith(CANCEL_RP_ALREADY_CANCELLED.uri)
174
175
  })
175
176
 
176
177
  it('returns value of redirect', async () => {
@@ -184,7 +185,7 @@ describe('Cancel RP Authentication Handler', () => {
184
185
  expect(result).toBe(redirectResult)
185
186
  })
186
187
 
187
- it('sets page cache error for RCP cancelled', async () => {
188
+ it('sets page cache for RCP already cancelled', async () => {
188
189
  const { request } = await invokeHandlerWithMocks({
189
190
  salesApiResponse: { permission: { id: 'perm-id' }, recurringPayment: { id: 'rcp-id', status: 1, cancelledDate: '2024-01-01' } }
190
191
  })
@@ -192,7 +193,7 @@ describe('Cancel RP Authentication Handler', () => {
192
193
  CANCEL_RP_IDENTIFY.page,
193
194
  expect.objectContaining({
194
195
  payload: expect.any(Object),
195
- error: { recurringPayment: 'rcp-cancelled' }
196
+ errorRedirect: true
196
197
  })
197
198
  )
198
199
  })
@@ -1,7 +1,9 @@
1
- import { CANCEL_RP_IDENTIFY, CANCEL_RP_DETAILS, CANCEL_RP_AGREEMENT_NOT_FOUND } from '../../src/uri.js'
1
+ import { CANCEL_RP_IDENTIFY, CANCEL_RP_DETAILS, CANCEL_RP_AGREEMENT_NOT_FOUND, CANCEL_RP_ALREADY_CANCELLED } from '../../src/uri.js'
2
2
  import { salesApi } from '@defra-fish/connectors-lib'
3
3
  import { validation } from '@defra-fish/business-rules-lib'
4
4
  import { setupCancelRecurringPaymentCacheFromAuthResult } from '../processors/recurring-payments-write-cache.js'
5
+ import { cacheDateFormat } from '../../src/processors/date-and-time-display.js'
6
+ import moment from 'moment-timezone'
5
7
  import Joi from 'joi'
6
8
 
7
9
  const applyAuthFailure = async (request, h, { pageData, redirectUri, statusData }) => {
@@ -35,7 +37,12 @@ const cancelRecurringPaymentAuthenticationHandler = async (request, h) => {
35
37
  context.pageData.errorRedirect = true
36
38
  context.redirectUri = CANCEL_RP_AGREEMENT_NOT_FOUND.uri
37
39
  } else if (authenticationResult.recurringPayment.cancelledDate) {
38
- context.pageData.error = { recurringPayment: 'rcp-cancelled' }
40
+ context.pageData.errorRedirect = true
41
+ context.redirectUri = CANCEL_RP_ALREADY_CANCELLED.uri
42
+ context.pageData.payload = {
43
+ ...context.pageData.payload,
44
+ endDate: moment(authenticationResult.recurringPayment.endDate).format(cacheDateFormat)
45
+ }
39
46
  }
40
47
 
41
48
  if (context.pageData.error || context.pageData.errorRedirect) {
@@ -786,9 +786,18 @@
786
786
  "role_required_body_1": "You were successfully signed in but you do not have the correct role assigned to access the telesales application.",
787
787
  "role_required_body_2": "Please contact your Microsoft Dynamics administrator to request access.",
788
788
  "role_required_title": "Role required",
789
+ "rp_licence_already_cancelled_heading": "Your recurring card payment agreement is already cancelled",
790
+ "rp_licence_already_cancelled_body_1": "Licence ending ",
791
+ "rp_licence_already_cancelled_body_1_1": " expires on ",
792
+ "rp_licence_already_cancelled_body_1_2": " and will not renew automatically. To continue fishing after this date you need to ",
793
+ "rp_licence_already_cancelled_body_link": "get a new licence (opens in new tab)",
794
+ "rp_licence_already_cancelled_body_2_1": "If you need help email ",
795
+ "rp_licence_already_cancelled_body_2_2": " or call 03708 506 506 (8am to 6pm, Monday to Friday, except public holidays).",
796
+ "rp_licence_already_cancelled_body_3_1": "Go back",
797
+ "rp_licence_already_cancelled_body_3_2": " to cancel the recurring card payment for a different licence.",
798
+ "rp_licence_already_cancelled_body_ea_link": "enquiries@environment-agency.gov.uk",
799
+ "rp_cancel_already_cancelled_title": "Already cancelled - Cancel your recurring card payment ",
789
800
  "rp_cancel_complete_title": "Cancel your recurring card payment agreement - complete",
790
- "rp_cancel_confirm_title": "Cancel your recurring card payment agreement - confirm",
791
- "rp_cancel_details_title": "Cancel your recurring card payment agreement - details",
792
801
  "rp_cancel_identify_title": "Cancel your recurring card payment agreement - identify",
793
802
  "rp_cancel_no_agreement_found_title": "Agreement not found - Cancel your recurring card payment",
794
803
  "rp_cancel_no_agreement_found_heading": "There is no recurring card payment linked to this licence",
@@ -786,9 +786,18 @@
786
786
  "role_required_body_1": "You were successfully signed in but you do not have the correct role assigned to access the telesales application.",
787
787
  "role_required_body_2": "Please contact your Microsoft Dynamics administrator to request access.",
788
788
  "role_required_title": "Role required",
789
+ "rp_licence_already_cancelled_heading": "Your recurring card payment agreement is already cancelled",
790
+ "rp_licence_already_cancelled_body_1": "Licence ending ",
791
+ "rp_licence_already_cancelled_body_1_1": " expires on ",
792
+ "rp_licence_already_cancelled_body_1_2": " and will not renew automatically. To continue fishing after this date you need to ",
793
+ "rp_licence_already_cancelled_body_link": "get a new licence (opens in new tab)",
794
+ "rp_licence_already_cancelled_body_2_1": "If you need help email ",
795
+ "rp_licence_already_cancelled_body_2_2": " or call 03708 506 506 (8am to 6pm, Monday to Friday, except public holidays).",
796
+ "rp_licence_already_cancelled_body_3_1": "Go back",
797
+ "rp_licence_already_cancelled_body_3_2": " to cancel the recurring card payment for a different licence.",
798
+ "rp_licence_already_cancelled_body_ea_link": "enquiries@environment-agency.gov.uk",
799
+ "rp_cancel_already_cancelled_title": "Already cancelled - Cancel your recurring card payment ",
789
800
  "rp_cancel_complete_title": "Cancel your recurring card payment agreement - complete",
790
- "rp_cancel_confirm_title": "Cancel your recurring card payment agreement - confirm",
791
- "rp_cancel_details_title": "Cancel your recurring card payment agreement - details",
792
801
  "rp_cancel_identify_title": "Cancel your recurring card payment agreement - identify",
793
802
  "rp_cancel_no_agreement_found_title": "Agreement not found - Cancel your recurring card payment",
794
803
  "rp_cancel_no_agreement_found_heading": "There is no recurring card payment linked to this licence",
@@ -12,4 +12,4 @@
12
12
  <p class="govuk-body">{{ mssgs.rp_cancel_no_agreement_found_need_help_1 }}<a class="govuk-link" href="mailto:{{ data.links.contactUs }}">{{ mssgs.rp_cancel_no_agreement_found_need_help_email }}</a>{{ mssgs.rp_cancel_no_agreement_found_need_help_2 }}<a class="govuk-link" href="{{ data.links.callCharges }}" target="_blank">{{ mssgs.rp_cancel_no_agreement_found_call_charges_link }}</a></p>
13
13
  </div>
14
14
  </div>
15
- {% endblock %}
15
+ {% endblock %}
@@ -0,0 +1,115 @@
1
+ import pageRoute from '../../../../../routes/page-route.js'
2
+ import { CANCEL_RP_ALREADY_CANCELLED } from '../../../../../uri.js'
3
+
4
+ require('../route.js')
5
+
6
+ // eslint-disable-next-line no-unused-vars
7
+ const getData = pageRoute.mock.calls[0][4]
8
+
9
+ jest.mock('../../../../../routes/page-route.js', () => jest.fn())
10
+ jest.mock('../../../../../uri.js', () => ({
11
+ ...jest.requireActual('../../../../../uri.js'),
12
+ CANCEL_RP_ALREADY_CANCELLED: {
13
+ page: Symbol('cancel-rp-already-cancelled page'),
14
+ uri: Symbol('cancel-rp-already-cancelled uri')
15
+ }
16
+ }))
17
+ jest.mock('../../../../../processors/uri-helper.js')
18
+
19
+ const getSampleRequest = referenceNumber => ({
20
+ cache: jest.fn(() => ({
21
+ helpers: {
22
+ page: {
23
+ getCurrentPermission: jest.fn(() => ({ payload: { referenceNumber, endDate: '2025:10:03:03:33:33' } }))
24
+ }
25
+ }
26
+ }))
27
+ })
28
+
29
+ describe('pageRoute receives expected arguments', () => {
30
+ it('passes CANCEL_RP_ALREADY_CANCELLED.page as the view name', () => {
31
+ jest.isolateModules(() => {
32
+ require('../route.js')
33
+ expect(pageRoute).toHaveBeenCalledWith(
34
+ CANCEL_RP_ALREADY_CANCELLED.page,
35
+ expect.anything(),
36
+ expect.anything(),
37
+ expect.anything(),
38
+ expect.anything()
39
+ )
40
+ })
41
+ })
42
+
43
+ it('passes CANCEL_RP_ALREADY_CANCELLED.uri as the path', () => {
44
+ jest.isolateModules(() => {
45
+ require('../route.js')
46
+ expect(pageRoute).toHaveBeenCalledWith(
47
+ expect.anything(),
48
+ CANCEL_RP_ALREADY_CANCELLED.uri,
49
+ expect.anything(),
50
+ expect.anything(),
51
+ expect.anything()
52
+ )
53
+ })
54
+ })
55
+
56
+ it('passes a function as the validator', () => {
57
+ jest.isolateModules(() => {
58
+ require('../route.js')
59
+ expect(pageRoute).toHaveBeenCalledWith(
60
+ expect.anything(),
61
+ expect.anything(),
62
+ expect.any(Function),
63
+ expect.anything(),
64
+ expect.anything()
65
+ )
66
+ })
67
+ })
68
+
69
+ it('passes a function to generate redirect location on completion', () => {
70
+ jest.isolateModules(() => {
71
+ require('../route.js')
72
+ expect(pageRoute).toHaveBeenCalledWith(
73
+ expect.anything(),
74
+ expect.anything(),
75
+ expect.anything(),
76
+ expect.any(Function),
77
+ expect.anything()
78
+ )
79
+ })
80
+ })
81
+
82
+ it('passes getData to pageRoute', () => {
83
+ jest.isolateModules(() => {
84
+ require('../route.js')
85
+ expect(pageRoute).toHaveBeenCalledWith(
86
+ expect.anything(),
87
+ expect.anything(),
88
+ expect.anything(),
89
+ expect.anything(),
90
+ expect.any(Function)
91
+ )
92
+ })
93
+ })
94
+ })
95
+
96
+ describe('getData', () => {
97
+ it('returns reference number from payload', async () => {
98
+ const referenceNumber = 'RP0310'
99
+ expect(await getData(getSampleRequest(referenceNumber))).toEqual(
100
+ expect.objectContaining({
101
+ referenceNumber
102
+ })
103
+ )
104
+ })
105
+
106
+ it('transforms end date to display format', async () => {
107
+ const endDate = '2025-10-03T03:33:33.000Z'
108
+ const request = getSampleRequest()
109
+ request.cache().helpers.page.getCurrentPermission.mockResolvedValueOnce({
110
+ payload: { referenceNumber: 'RP0310', endDate }
111
+ })
112
+ const data = await getData(request)
113
+ expect(data.endDate).toEqual('3 October 2025')
114
+ })
115
+ })
@@ -0,0 +1,28 @@
1
+ {% extends "layout.njk" %}
2
+ {% from "page-title.njk" import pageTitle %}
3
+
4
+ {% set title = mssgs.rp_cancel_already_cancelled_title %}
5
+ {% block pageTitle %}{{ pageTitle(title, mssgs) }}{% endblock %}
6
+
7
+ {% block content %}
8
+ <div class="govuk-grid-row">
9
+ <div class="govuk-grid-column-two-thirds">
10
+ <h1 class="govuk-heading-l">{{ mssgs.rp_licence_already_cancelled_heading }}</h1>
11
+ <p class="govuk-body">
12
+ {{ mssgs.rp_licence_already_cancelled_body_1 }}{{ data.referenceNumber }}{{ mssgs.rp_licence_already_cancelled_body_1_1 }}{{ data.endDate }}{{ mssgs.rp_licence_already_cancelled_body_1_2 }}
13
+ <a href="/" class="govuk-link" target="_blank" rel="noopener"> {{ mssgs.rp_licence_already_cancelled_body_link }}</a>{{ mssgs.full_stop }}
14
+ </p>
15
+ <p class="govuk-body">
16
+ {{ mssgs.rp_licence_already_cancelled_body_2_1 }}
17
+ <a href="mailto:enquiries@environment-agency.gov.uk"> {{ mssgs.rp_licence_already_cancelled_body_ea_link }}</a>
18
+ {{ mssgs.rp_licence_already_cancelled_body_2_2 }}
19
+ <a href="https://www.gov.uk/call-charges" target="_blank" rel="noopener"> {{ mssgs.licence_not_found_body_call_charges_link }}</a>{{ mssgs.full_stop }}
20
+ </p>
21
+ <p class="govuk-body">
22
+ <a class="govuk-link" href="/buy/cancel-recurring-payment/identify"> {{ mssgs.rp_licence_already_cancelled_body_3_1 }}
23
+ </a>{{ mssgs.rp_licence_already_cancelled_body_3_2 }}
24
+ </p>
25
+
26
+ </div>
27
+ </div>
28
+ {% endblock %}
@@ -0,0 +1,25 @@
1
+ import pageRoute from '../../../../routes/page-route.js'
2
+ import { SERVICE_LOCAL_TIME } from '@defra-fish/business-rules-lib'
3
+ import { CANCEL_RP_ALREADY_CANCELLED, CANCEL_RP_IDENTIFY } from '../../../../uri.js'
4
+ import { cacheDateFormat, dateDisplayFormat } from '../../../../processors/date-and-time-display.js'
5
+ import moment from 'moment-timezone'
6
+
7
+ const getData = async request => {
8
+ const {
9
+ payload: { referenceNumber, endDate }
10
+ } = await request.cache().helpers.page.getCurrentPermission(CANCEL_RP_IDENTIFY.page)
11
+ const endDateString = moment(endDate, cacheDateFormat).tz(SERVICE_LOCAL_TIME).format(dateDisplayFormat)
12
+
13
+ return {
14
+ referenceNumber,
15
+ endDate: endDateString
16
+ }
17
+ }
18
+
19
+ export default pageRoute(
20
+ CANCEL_RP_ALREADY_CANCELLED.page,
21
+ CANCEL_RP_ALREADY_CANCELLED.uri,
22
+ () => {},
23
+ () => {},
24
+ getData
25
+ )
@@ -116,6 +116,22 @@ Object {
116
116
  },
117
117
  "path": "/buy/cancel-recurring-payment/agreement-not-found",
118
118
  },
119
+ Object {
120
+ "handler": [Function],
121
+ "method": "GET",
122
+ "path": "/buy/cancel-recurring-payment/already-cancelled",
123
+ },
124
+ Object {
125
+ "handler": [Function],
126
+ "method": "POST",
127
+ "options": Object {
128
+ "validate": Object {
129
+ "failAction": [Function],
130
+ "payload": [Function],
131
+ },
132
+ },
133
+ "path": "/buy/cancel-recurring-payment/already-cancelled",
134
+ },
119
135
  ],
120
136
  }
121
137
  `;
@@ -34,7 +34,8 @@ import {
34
34
  CANCEL_RP_DETAILS,
35
35
  CANCEL_RP_CONFIRM,
36
36
  CANCEL_RP_COMPLETE,
37
- CANCEL_RP_AGREEMENT_NOT_FOUND
37
+ CANCEL_RP_AGREEMENT_NOT_FOUND,
38
+ CANCEL_RP_ALREADY_CANCELLED
38
39
  } from '../uri.js'
39
40
 
40
41
  import { CommonResults, CONTACT_SUMMARY_SEEN, ShowDigitalLicencePages } from '../constants.js'
@@ -463,5 +464,9 @@ export default [
463
464
  {
464
465
  current: CANCEL_RP_AGREEMENT_NOT_FOUND,
465
466
  backLink: CANCEL_RP_IDENTIFY.uri
467
+ },
468
+ {
469
+ current: CANCEL_RP_ALREADY_CANCELLED,
470
+ backLink: CANCEL_RP_IDENTIFY.uri
466
471
  }
467
472
  ]
@@ -5,6 +5,7 @@ import cancelRPDetails from '../pages/recurring-payments/cancel/details/route.js
5
5
  import cancelRPConfirm from '../pages/recurring-payments/cancel/confirm/route.js'
6
6
  import cancelRPComplete from '../pages/recurring-payments/cancel/complete/route.js'
7
7
  import cancelRPAgreementNotFound from '../pages/recurring-payments/cancel/agreement-not-found/route.js'
8
+ import cancelRPAlreadyCancelled from '../pages/recurring-payments/cancel/already-cancelled/route.js'
8
9
 
9
10
  const telesalesRoutes = [
10
11
  {
@@ -33,6 +34,13 @@ const telesalesRoutes = [
33
34
  ]
34
35
 
35
36
  if (process.env.SHOW_CANCELLATION_JOURNEY === 'true') {
36
- telesalesRoutes.push(...cancelRPIdentify, ...cancelRPDetails, ...cancelRPConfirm, ...cancelRPComplete, ...cancelRPAgreementNotFound)
37
+ telesalesRoutes.push(
38
+ ...cancelRPIdentify,
39
+ ...cancelRPDetails,
40
+ ...cancelRPConfirm,
41
+ ...cancelRPComplete,
42
+ ...cancelRPAgreementNotFound,
43
+ ...cancelRPAlreadyCancelled
44
+ )
37
45
  }
38
46
  export default telesalesRoutes
package/src/uri.js CHANGED
@@ -62,6 +62,9 @@ export const ERROR_TESTING = { uri: '/buy/throw-error' }
62
62
  export const CHOOSE_PAYMENT = { uri: '/buy/choose-payment', page: 'choose-payment' }
63
63
  export const SET_UP_PAYMENT = { uri: '/buy/set-up-recurring-card-payment', page: 'set-up-payment' }
64
64
 
65
+ /**
66
+ * Recurring payments cancellation pages
67
+ */
65
68
  export const CANCEL_RP_IDENTIFY = { uri: '/buy/cancel-recurring-payment/identify', page: 'cancel-rp-identify' }
66
69
  export const CANCEL_RP_AUTHENTICATE = { uri: '/buy/cancel-recurring-payment/authenticate' }
67
70
  export const CANCEL_RP_DETAILS = { uri: '/buy/cancel-recurring-payment/details', page: 'cancel-rp-details' }
@@ -71,6 +74,7 @@ export const CANCEL_RP_AGREEMENT_NOT_FOUND = {
71
74
  uri: '/buy/cancel-recurring-payment/agreement-not-found',
72
75
  page: 'cancel-rp-agreement-not-found'
73
76
  }
77
+ export const CANCEL_RP_ALREADY_CANCELLED = { uri: '/buy/cancel-recurring-payment/already-cancelled', page: 'already-cancelled' }
74
78
 
75
79
  /**
76
80
  * These are informational static pages