@defra-fish/gafl-webapp-service 1.69.0 → 1.70.0-rc.1

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.69.0",
3
+ "version": "1.70.0-rc.1",
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.69.0",
40
- "@defra-fish/connectors-lib": "1.69.0",
39
+ "@defra-fish/business-rules-lib": "1.70.0-rc.1",
40
+ "@defra-fish/connectors-lib": "1.70.0-rc.1",
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": "6cf7d2cbc2c4f285f9c3f8f6ae52ab1c2f735ab6"
82
+ "gitHead": "1dafe1a0ea9e0558cc1f6515584dd90c74509858"
83
83
  }
@@ -1,11 +1,11 @@
1
- import agreedHandler from '../agreed-handler.js'
2
1
  import { salesApi } from '@defra-fish/connectors-lib'
3
2
  import { prepareApiTransactionPayload, prepareApiFinalisationPayload } from '../../processors/api-transaction.js'
4
3
  import { sendPayment, getPaymentStatus, sendRecurringPayment } from '../../services/payment/govuk-pay-service.js'
5
4
  import { preparePayment, prepareRecurringPaymentAgreement } from '../../processors/payment.js'
6
5
  import { COMPLETION_STATUS, RECURRING_PAYMENT } from '../../constants.js'
7
- import { ORDER_COMPLETE, PAYMENT_CANCELLED, PAYMENT_FAILED } from '../../uri.js'
6
+ import { ORDER_COMPLETE, PAYMENT_CANCELLED, PAYMENT_FAILED, CONTROLLER } from '../../uri.js'
8
7
  import { PAYMENT_JOURNAL_STATUS_CODES, GOVUK_PAY_ERROR_STATUS_CODES } from '@defra-fish/business-rules-lib'
8
+ import agreedHandler from '../agreed-handler.js'
9
9
  import Boom from '@hapi/boom'
10
10
 
11
11
  jest.mock('@defra-fish/connectors-lib', () => ({
@@ -760,4 +760,24 @@ describe('agreed-handler', () => {
760
760
  await expect(agreedHandler(request, h)).rejects.toThrow(Boom.Forbidden)
761
761
  })
762
762
  })
763
+
764
+ describe('missing session data', () => {
765
+ it.each([
766
+ ['transaction is null', null, {}],
767
+ ['status is null', {}, null],
768
+ ['both transaction and status are null', null, null]
769
+ ])('redirects to the controller when %s', async (_, transaction, status) => {
770
+ const h = getMockResponseToolkit()
771
+ const request = getMockRequest({
772
+ cache: () => ({
773
+ helpers: {
774
+ transaction: { get: async () => transaction },
775
+ status: { get: async () => status }
776
+ }
777
+ })
778
+ })
779
+ await agreedHandler(request, h)
780
+ expect(h.redirectWithLanguageCode).toHaveBeenCalledWith(CONTROLLER.uri)
781
+ })
782
+ })
763
783
  })
@@ -16,7 +16,7 @@ import { prepareApiTransactionPayload, prepareApiFinalisationPayload } from '../
16
16
  import { sendPayment, getPaymentStatus, sendRecurringPayment } from '../services/payment/govuk-pay-service.js'
17
17
  import { preparePayment, prepareRecurringPaymentAgreement } from '../processors/payment.js'
18
18
  import { COMPLETION_STATUS, RECURRING_PAYMENT } from '../constants.js'
19
- import { ORDER_COMPLETE, PAYMENT_CANCELLED, PAYMENT_FAILED } from '../uri.js'
19
+ import { ORDER_COMPLETE, PAYMENT_CANCELLED, PAYMENT_FAILED, CONTROLLER } from '../uri.js'
20
20
  import { PAYMENT_JOURNAL_STATUS_CODES, GOVUK_PAY_ERROR_STATUS_CODES } from '@defra-fish/business-rules-lib'
21
21
  import { v4 as uuidv4 } from 'uuid'
22
22
  const debug = db('webapp:agreed-handler')
@@ -240,6 +240,19 @@ const finaliseTransaction = async (request, transaction, status) => {
240
240
  }
241
241
  }
242
242
 
243
+ const postTransaction = async (request, transaction, status) => {
244
+ // Create the agreement if a recurring payment
245
+ if (status[RECURRING_PAYMENT] === true) {
246
+ await createRecurringPayment(request, transaction, status)
247
+ }
248
+ try {
249
+ await sendToSalesApi(request, transaction, status)
250
+ } catch (e) {
251
+ debug('Error sending transaction to Sales Api', JSON.stringify(transaction), JSON.stringify(status))
252
+ throw e
253
+ }
254
+ }
255
+
243
256
  /**
244
257
  * Agreed route handler
245
258
  * @param request
@@ -249,6 +262,12 @@ const finaliseTransaction = async (request, transaction, status) => {
249
262
  export default async (request, h) => {
250
263
  const status = await request.cache().helpers.status.get()
251
264
  const transaction = await request.cache().helpers.transaction.get()
265
+
266
+ if (!transaction || !status) {
267
+ debug('Session data missing in agreed handler - transaction: %s, status: %s', !!transaction, !!status)
268
+ return h.redirectWithLanguageCode(CONTROLLER.uri)
269
+ }
270
+
252
271
  if (!transaction.id) {
253
272
  transaction.id = uuidv4()
254
273
  }
@@ -260,16 +279,7 @@ export default async (request, h) => {
260
279
 
261
280
  // Send the transaction to the sales API and process the response
262
281
  if (!status[COMPLETION_STATUS.posted]) {
263
- // Create the agreement if a recurring payment
264
- if (status[RECURRING_PAYMENT] === true) {
265
- await createRecurringPayment(request, transaction, status)
266
- }
267
- try {
268
- await sendToSalesApi(request, transaction, status)
269
- } catch (e) {
270
- debug('Error sending transaction to Sales Api', JSON.stringify(transaction), JSON.stringify(status))
271
- throw e
272
- }
282
+ await postTransaction(request, transaction, status)
273
283
  }
274
284
 
275
285
  // The payment section is ignored for zero cost transactions
@@ -798,7 +798,6 @@
798
798
  "rp_licence_already_cancelled_body_3_2": " to cancel the recurring card payment for a different licence.",
799
799
  "rp_licence_already_cancelled_body_ea_link": "enquiries@environment-agency.gov.uk",
800
800
  "rp_cancel_already_cancelled_title": "Already cancelled - Cancel your recurring card payment ",
801
- "rp_cancel_complete_title": "Cancel your recurring card payment agreement - complete",
802
801
  "rp_cancel_identify_title": "Cancel your recurring card payment agreement - identify",
803
802
  "rp_cancel_no_agreement_found_title": "Agreement not found - Cancel your recurring card payment",
804
803
  "rp_cancel_no_agreement_found_heading": "There is no recurring card payment linked to this licence",
@@ -821,17 +820,20 @@
821
820
  "rp_cancel_details_payment_card": "Payment card (last 4 digits)",
822
821
  "rp_cancel_details_title": "Check your details",
823
822
  "rp_cancel_details_summary_title": "If you need help checking your details",
824
- "rp_cancel_complete_panel_title": "Your recurring card payment agreement has been cancelled",
825
- "rp_cancel_complete_body_1": "We'll send confirmation by email.",
826
- "rp_cancel_complete_body_2_1": "To continue fishing after your current licence expires on",
827
- "rp_cancel_complete_body_2_2": "you will need to",
828
- "rp_cancel_complete_body_2_link": "buy another licence (opens in new tab)",
829
- "rp_cancel_complete_body_3_header": "Before you leave this page",
830
- "rp_cancel_complete_body_3_paragraph": "You may like to:",
831
- "rp_cancel_complete_body_3_bulletpoint_1_link": "help us improve this service (opens in new tab)",
832
- "rp_cancel_complete_body_3_bulletpoint_1": "takes 30 seconds",
833
- "rp_cancel_complete_body_3_bulletpoint_2_link": "read the fisheries annual report (opens in new tab)",
834
- "rp_cancel_complete_body_3_bulletpoint_2": "to see how we spend income from fishing licences",
823
+ "rp_cancel_complete_panel_title": "Mae eich cytundeb taliad cerdyn rheolaidd wedi’i ganslo",
824
+ "rp_cancel_complete_body_1": "Byddwn yn anfon cadarnhad drwy ",
825
+ "rp_cancel_complete_method_email": "e-bost.",
826
+ "rp_cancel_complete_method_text": "neges destun.",
827
+ "rp_cancel_complete_method_letter": "lythyr.",
828
+ "rp_cancel_complete_body_2_1": "I barhau i bysgota ar ôl i’ch trwydded gyfredol ddod i ben ar",
829
+ "rp_cancel_complete_body_2_2": "bydd angen i chi",
830
+ "rp_cancel_complete_body_2_link": "brynu trwydded arall (yn agor mewn tab newydd)",
831
+ "rp_cancel_complete_body_3_header": "Cyn gadael y dudalen hon",
832
+ "rp_cancel_complete_body_3_paragraph": "Efallai yr hoffech:",
833
+ "rp_cancel_complete_body_3_bulletpoint_1_link": "ein helpu i wella’r gwasanaeth hwn (yn agor mewn tab newydd)",
834
+ "rp_cancel_complete_body_3_bulletpoint_1": "yn cymryd 30 eiliad",
835
+ "rp_cancel_complete_body_3_bulletpoint_2_link": "darllen yr adroddiad blynyddol ar bysgodfeydd (yn agor mewn tab newydd)",
836
+ "rp_cancel_complete_body_3_bulletpoint_2": "i weld sut rydym yn gwario incwm o drwyddedau pysgota",
835
837
  "save_changes": "Cadw newidiadau",
836
838
  "server_error_title_suffix": " - GOV.UK",
837
839
  "server_error_bulletpoint_1": "Os ydych chi wedi gofyn am hysbysiad dros e-bost neu neges destun, dylai’r hysbysiad gyrraedd o fewn 3 awr os bydd y taliad wedi cael ei gymryd.",
@@ -810,7 +810,6 @@
810
810
  "rp_licence_already_cancelled_body_3_2": " to cancel the recurring card payment for a different licence.",
811
811
  "rp_licence_already_cancelled_body_ea_link": "enquiries@environment-agency.gov.uk",
812
812
  "rp_cancel_already_cancelled_title": "Already cancelled - Cancel your recurring card payment ",
813
- "rp_cancel_complete_title": "Cancel your recurring card payment agreement - complete",
814
813
  "rp_cancel_identify_title": "Cancel your recurring card payment agreement - identify",
815
814
  "rp_cancel_no_agreement_found_title": "Agreement not found - Cancel your recurring card payment",
816
815
  "rp_cancel_no_agreement_found_heading": "There is no recurring card payment linked to this licence",
@@ -834,7 +833,10 @@
834
833
  "rp_cancel_details_title": "Check your details",
835
834
  "rp_cancel_details_summary_title": "If you need help checking your details",
836
835
  "rp_cancel_complete_panel_title": "Your recurring card payment agreement has been cancelled",
837
- "rp_cancel_complete_body_1": "We'll send confirmation by email.",
836
+ "rp_cancel_complete_body_1": "We'll send confirmation by ",
837
+ "rp_cancel_complete_contact_method_email": "email.",
838
+ "rp_cancel_complete_contact_method_text": "text message.",
839
+ "rp_cancel_complete_contact_method_letter": "letter.",
838
840
  "rp_cancel_complete_body_2_1": "To continue fishing after your current licence expires on",
839
841
  "rp_cancel_complete_body_2_2": "you will need to",
840
842
  "rp_cancel_complete_body_2_link": "buy another licence (opens in new tab)",
@@ -101,12 +101,22 @@ describe('completion handler', () => {
101
101
  })
102
102
 
103
103
  describe('getData', () => {
104
+ const mockMessages = {
105
+ rp_cancel_complete_contact_method_email: Symbol('email method'),
106
+ rp_cancel_complete_contact_method_text: Symbol('text method'),
107
+ rp_cancel_complete_contact_method_letter: Symbol('letter method')
108
+ }
109
+
104
110
  const getMockRequest = ({
105
111
  locale = 'cy',
106
112
  endDate = '2026-02-03',
113
+ preferredMethodOfConfirmation = 'Email',
107
114
  getCurrentPermission = jest.fn(() => ({
108
115
  permission: {
109
- endDate
116
+ endDate,
117
+ licensee: {
118
+ preferredMethodOfConfirmation
119
+ }
110
120
  }
111
121
  }))
112
122
  } = {}) => ({
@@ -117,7 +127,10 @@ describe('getData', () => {
117
127
  getCurrentPermission
118
128
  }
119
129
  }
120
- })
130
+ }),
131
+ i18n: {
132
+ getCatalog: jest.fn(() => mockMessages)
133
+ }
121
134
  })
122
135
 
123
136
  beforeEach(() => {
@@ -125,7 +138,9 @@ describe('getData', () => {
125
138
  })
126
139
 
127
140
  it('retrieves the current permission once', async () => {
128
- const getCurrentPermission = jest.fn(() => ({ permission: { endDate: 'end date' } }))
141
+ const getCurrentPermission = jest.fn(() => ({
142
+ permission: { endDate: 'end date', licensee: { preferredMethodOfConfirmation: 'Email' } }
143
+ }))
129
144
  const request = getMockRequest({ getCurrentPermission })
130
145
 
131
146
  await getData(request)
@@ -158,6 +173,30 @@ describe('getData', () => {
158
173
 
159
174
  const data = await getData(getMockRequest())
160
175
 
161
- expect(data).toEqual({ licenceExpiry: formattedLicenceExpiry })
176
+ expect(data).toEqual(expect.objectContaining({ licenceExpiry: formattedLicenceExpiry }))
177
+ })
178
+
179
+ it('returns email method when preferredMethodOfConfirmation is Email', async () => {
180
+ const data = await getData(getMockRequest({ preferredMethodOfConfirmation: 'Email' }))
181
+
182
+ expect(data.preferredMethodOfContact).toBe(mockMessages.rp_cancel_complete_contact_method_email)
183
+ })
184
+
185
+ it('returns text message method when preferredMethodOfConfirmation is Text', async () => {
186
+ const data = await getData(getMockRequest({ preferredMethodOfConfirmation: 'Text' }))
187
+
188
+ expect(data.preferredMethodOfContact).toBe(mockMessages.rp_cancel_complete_contact_method_text)
189
+ })
190
+
191
+ it('returns letter method when preferredMethodOfConfirmation is Letter', async () => {
192
+ const data = await getData(getMockRequest({ preferredMethodOfConfirmation: 'Letter' }))
193
+
194
+ expect(data.preferredMethodOfContact).toBe(mockMessages.rp_cancel_complete_contact_method_letter)
195
+ })
196
+
197
+ it('defaults to letter method for unknown preference', async () => {
198
+ const data = await getData(getMockRequest({ preferredMethodOfConfirmation: 'Prefer not to be contacted' }))
199
+
200
+ expect(data.preferredMethodOfContact).toBe(mockMessages.rp_cancel_complete_contact_method_letter)
162
201
  })
163
202
  })
@@ -1,7 +1,7 @@
1
1
  {% extends "layout.njk" %}
2
2
  {% from "page-title.njk" import pageTitle %}
3
3
  {% from "panel/macro.njk" import govukPanel %}
4
- {% set title = mssgs.rp_cancel_complete_title %}
4
+ {% set title = mssgs.rp_cancel_complete_panel_title %}
5
5
  {% block pageTitle %}
6
6
  {{ pageTitle(title, error, mssgs) }}
7
7
  {% endblock %}
@@ -12,7 +12,7 @@
12
12
  titleText: mssgs.rp_cancel_complete_panel_title,
13
13
  classes: "govuk-!-margin-bottom-9"
14
14
  }) }}
15
- <p class="govuk-body">{{ mssgs.rp_cancel_complete_body_1 }}</p>
15
+ <p class="govuk-body">{{ mssgs.rp_cancel_complete_body_1 }}{{ data.preferredMethodOfContact }}</p>
16
16
  <p class="govuk-body">
17
17
  {{ mssgs.rp_cancel_complete_body_2_1 }} {{ data.licenceExpiry }} {{ mssgs.rp_cancel_complete_body_2_2 }}
18
18
  <a href="https://www.gov.uk/fishing-licences/buy-a-fishing-licence" target="_blank" rel="noopener">{{ mssgs.rp_cancel_complete_body_2_link }}</a>.
@@ -2,12 +2,25 @@ import pageRoute from '../../../../routes/page-route.js'
2
2
  import { CANCEL_RP_COMPLETE } from '../../../../uri.js'
3
3
  import { cacheDateFormat, dateDisplayFormat } from '../../../../processors/date-and-time-display.js'
4
4
  import moment from 'moment-timezone'
5
+ import { HOW_CONTACTED } from '../../../../processors/mapping-constants.js'
6
+
7
+ const getPreferredMethodOfContact = (preference, mssgs) => {
8
+ if (preference === HOW_CONTACTED.email) {
9
+ return mssgs.rp_cancel_complete_contact_method_email
10
+ }
11
+ if (preference === HOW_CONTACTED.text) {
12
+ return mssgs.rp_cancel_complete_contact_method_text
13
+ }
14
+ return mssgs.rp_cancel_complete_contact_method_letter
15
+ }
5
16
 
6
17
  const getData = async request => {
7
18
  const { permission } = await request.cache().helpers.transaction.getCurrentPermission()
19
+ const mssgs = request.i18n.getCatalog()
8
20
 
9
21
  return {
10
- licenceExpiry: moment(permission.endDate, cacheDateFormat, request.locale).format(dateDisplayFormat)
22
+ licenceExpiry: moment(permission.endDate, cacheDateFormat, request.locale).format(dateDisplayFormat),
23
+ preferredMethodOfContact: getPreferredMethodOfContact(permission.licensee.preferredMethodOfConfirmation, mssgs)
11
24
  }
12
25
  }
13
26
 
@@ -5,7 +5,11 @@ describe('setUpCancelRecurringPaymentCacheFromAuthenticationResult', () => {
5
5
  const defaults = {
6
6
  referenceNumber: '23270624-2WC3FSD-ABNCY4',
7
7
  endDate: '2024-12-31',
8
- licensee: { firstName: 'Brenin', lastName: 'Pysgotwr' },
8
+ licensee: {
9
+ firstName: 'Brenin',
10
+ lastName: 'Pysgotwr',
11
+ preferredMethodOfConfirmation: { id: 910400000, label: 'Email', description: 'Email' }
12
+ },
9
13
  permit: { description: 'Coarse 6 month 15 Rod Licence (Half)' },
10
14
  recurringPayment: { lastDigitsCardNumbers: '5678' }
11
15
  }
@@ -44,7 +48,6 @@ describe('setUpCancelRecurringPaymentCacheFromAuthenticationResult', () => {
44
48
  it.each([
45
49
  ['referenceNumber', '23270624-2WC3FSD-ABNCY4'],
46
50
  ['endDate', '2024-12-31'],
47
- ['licensee', { firstName: 'John', lastName: 'Bull' }],
48
51
  ['permit', { description: 'Coarse 12 month 2 Rod Licence (Full)' }]
49
52
  ])("Adds permission %s, value '%s', to transaction cache", async (fieldName, fieldValue) => {
50
53
  const setCurrentPermission = jest.fn()
@@ -62,6 +65,28 @@ describe('setUpCancelRecurringPaymentCacheFromAuthenticationResult', () => {
62
65
  )
63
66
  })
64
67
 
68
+ it('Adds licensee firstName, lastName and preferredMethodOfConfirmation label to transaction cache', async () => {
69
+ const setCurrentPermission = jest.fn()
70
+ const mockRequest = getSampleRequest(setCurrentPermission)
71
+ const authResult = getSampleAuthResult({
72
+ licensee: {
73
+ firstName: 'John',
74
+ lastName: 'Bull',
75
+ preferredMethodOfConfirmation: { id: 910400001, label: 'Text', description: 'Text' }
76
+ }
77
+ })
78
+
79
+ await setupCancelRecurringPaymentCacheFromAuthResult(mockRequest, authResult)
80
+
81
+ expect(setCurrentPermission).toHaveBeenCalledWith(
82
+ expect.objectContaining({
83
+ permission: expect.objectContaining({
84
+ licensee: { firstName: 'John', lastName: 'Bull', preferredMethodOfConfirmation: 'Text' }
85
+ })
86
+ })
87
+ )
88
+ })
89
+
65
90
  it.each([
66
91
  ['licensee', { anotherProperty: 'Should not be there' }],
67
92
  ['permit', { altProp: 'Should not be here' }]
@@ -6,7 +6,11 @@ export const setupCancelRecurringPaymentCacheFromAuthResult = async (request, au
6
6
  permission: {
7
7
  referenceNumber,
8
8
  endDate,
9
- licensee: { firstName: licensee.firstName, lastName: licensee.lastName },
9
+ licensee: {
10
+ firstName: licensee.firstName,
11
+ lastName: licensee.lastName,
12
+ preferredMethodOfConfirmation: licensee.preferredMethodOfConfirmation?.label
13
+ },
10
14
  permit: { description: permit.description }
11
15
  },
12
16
  recurringPayment: {