@defra-fish/sales-api-service 1.61.0-rc.0 → 1.61.0-rc.10
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 +5 -5
- package/src/schema/__tests__/recurring-payments.schema.spec.js +28 -1
- package/src/schema/recurring-payments.schema.js +5 -0
- package/src/server/routes/__tests__/recurring-payments.spec.js +47 -10
- package/src/server/routes/recurring-payments.js +30 -3
- package/src/services/__tests__/recurring-payments.service.spec.js +47 -5
- package/src/services/recurring-payments.service.js +21 -1
- package/src/services/transactions/__tests__/process-transaction-queue.spec.js +7 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defra-fish/sales-api-service",
|
|
3
|
-
"version": "1.61.0-rc.
|
|
3
|
+
"version": "1.61.0-rc.10",
|
|
4
4
|
"description": "Rod Licensing Sales API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@defra-fish/business-rules-lib": "1.61.0-rc.
|
|
39
|
-
"@defra-fish/connectors-lib": "1.61.0-rc.
|
|
40
|
-
"@defra-fish/dynamics-lib": "1.61.0-rc.
|
|
38
|
+
"@defra-fish/business-rules-lib": "1.61.0-rc.10",
|
|
39
|
+
"@defra-fish/connectors-lib": "1.61.0-rc.10",
|
|
40
|
+
"@defra-fish/dynamics-lib": "1.61.0-rc.10",
|
|
41
41
|
"@hapi/boom": "^9.1.2",
|
|
42
42
|
"@hapi/hapi": "^20.1.3",
|
|
43
43
|
"@hapi/inert": "^6.0.3",
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
"moment-timezone": "^0.5.34",
|
|
53
53
|
"uuid": "^8.3.2"
|
|
54
54
|
},
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "27650c5d19c86f1621c7c1367fafb7d090da3d02"
|
|
56
56
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dueRecurringPaymentsRequestParamsSchema,
|
|
3
3
|
dueRecurringPaymentsResponseSchema,
|
|
4
|
-
processRPResultRequestParamsSchema
|
|
4
|
+
processRPResultRequestParamsSchema,
|
|
5
|
+
linkRecurringPaymentsRequestParamsSchema
|
|
5
6
|
} from '../recurring-payments.schema.js'
|
|
6
7
|
|
|
7
8
|
jest.mock('../validators/validators.js', () => ({
|
|
@@ -62,6 +63,11 @@ const getProcessRPResultSampleData = () => ({
|
|
|
62
63
|
createdDate: '2025-01-01T00:00:00.000Z'
|
|
63
64
|
})
|
|
64
65
|
|
|
66
|
+
const getLinkRecurringPaymentsSampleData = () => ({
|
|
67
|
+
existingRecurringPaymentId: 'ghi123',
|
|
68
|
+
agreementId: 'jkl456'
|
|
69
|
+
})
|
|
70
|
+
|
|
65
71
|
describe('getDueRecurringPaymentsSchema', () => {
|
|
66
72
|
it('validates expected object', async () => {
|
|
67
73
|
expect(() => dueRecurringPaymentsResponseSchema.validateAsync(getResponseSampleData())).not.toThrow()
|
|
@@ -149,3 +155,24 @@ describe('processRPResultRequestParamsSchema', () => {
|
|
|
149
155
|
expect(() => processRPResultRequestParamsSchema.validateAsync(sampleData).rejects.toThrow())
|
|
150
156
|
})
|
|
151
157
|
})
|
|
158
|
+
|
|
159
|
+
describe('linkRecurringPaymentsRequestParamsSchema', () => {
|
|
160
|
+
it('validates expected object', async () => {
|
|
161
|
+
expect(() => linkRecurringPaymentsRequestParamsSchema.validateAsync(getLinkRecurringPaymentsSampleData())).not.toThrow()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it.each([['existingRecurringPaymentId'], ['agreementId']])('throws an error if %s is missing', async property => {
|
|
165
|
+
const sampleData = getLinkRecurringPaymentsSampleData()
|
|
166
|
+
delete sampleData[property]
|
|
167
|
+
expect(() => linkRecurringPaymentsRequestParamsSchema.validateAsync(sampleData).rejects.toThrow())
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it.each([
|
|
171
|
+
['existingRecurringPaymentId', 99],
|
|
172
|
+
['agreementId', 99]
|
|
173
|
+
])('throws an error if %s is not the correct type', async (property, value) => {
|
|
174
|
+
const sampleData = getLinkRecurringPaymentsSampleData()
|
|
175
|
+
sampleData[property] = value
|
|
176
|
+
expect(() => linkRecurringPaymentsRequestParamsSchema.validateAsync(sampleData).rejects.toThrow())
|
|
177
|
+
})
|
|
178
|
+
})
|
|
@@ -29,3 +29,8 @@ export const processRPResultRequestParamsSchema = Joi.object({
|
|
|
29
29
|
paymentId: Joi.string().required(),
|
|
30
30
|
createdDate: Joi.string().isoDate().required()
|
|
31
31
|
})
|
|
32
|
+
|
|
33
|
+
export const linkRecurringPaymentsRequestParamsSchema = Joi.object({
|
|
34
|
+
existingRecurringPaymentId: Joi.string().required(),
|
|
35
|
+
agreementId: Joi.string().required()
|
|
36
|
+
})
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import recurringPayments from '../recurring-payments.js'
|
|
2
|
-
import { getRecurringPayments, processRPResult } from '../../../services/recurring-payments.service.js'
|
|
3
|
-
import {
|
|
2
|
+
import { getRecurringPayments, processRPResult, linkRecurringPayments } from '../../../services/recurring-payments.service.js'
|
|
3
|
+
import {
|
|
4
|
+
dueRecurringPaymentsRequestParamsSchema,
|
|
5
|
+
processRPResultRequestParamsSchema,
|
|
6
|
+
linkRecurringPaymentsRequestParamsSchema
|
|
7
|
+
} from '../../../schema/recurring-payments.schema.js'
|
|
4
8
|
|
|
5
9
|
const [
|
|
6
10
|
{
|
|
@@ -8,26 +12,33 @@ const [
|
|
|
8
12
|
},
|
|
9
13
|
{
|
|
10
14
|
options: { handler: prpHandler }
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
options: { handler: lrpHandler }
|
|
11
18
|
}
|
|
12
19
|
] = recurringPayments
|
|
13
20
|
|
|
14
21
|
jest.mock('../../../services/recurring-payments.service.js', () => ({
|
|
15
22
|
getRecurringPayments: jest.fn(),
|
|
16
|
-
processRPResult: jest.fn()
|
|
23
|
+
processRPResult: jest.fn(),
|
|
24
|
+
linkRecurringPayments: jest.fn()
|
|
17
25
|
}))
|
|
18
26
|
|
|
19
27
|
jest.mock('../../../schema/recurring-payments.schema.js', () => ({
|
|
20
28
|
dueRecurringPaymentsRequestParamsSchema: jest.fn(),
|
|
21
|
-
processRPResultRequestParamsSchema: jest.fn()
|
|
29
|
+
processRPResultRequestParamsSchema: jest.fn(),
|
|
30
|
+
linkRecurringPaymentsRequestParamsSchema: jest.fn()
|
|
22
31
|
}))
|
|
23
32
|
|
|
24
33
|
const getMockRequest = ({
|
|
25
34
|
date = '2023-10-19',
|
|
26
35
|
transactionId = 'transaction-id',
|
|
27
36
|
paymentId = 'payment-id',
|
|
28
|
-
createdDate = 'created-date'
|
|
37
|
+
createdDate = 'created-date',
|
|
38
|
+
existingRecurringPaymentId = 'existing-recurring-payment-id',
|
|
39
|
+
agreementId = 'agreement-id'
|
|
29
40
|
}) => ({
|
|
30
|
-
params: { date, transactionId, paymentId, createdDate }
|
|
41
|
+
params: { date, transactionId, paymentId, createdDate, existingRecurringPaymentId, agreementId }
|
|
31
42
|
})
|
|
32
43
|
|
|
33
44
|
const getMockResponseToolkit = () => ({
|
|
@@ -75,11 +86,37 @@ describe('recurring payments', () => {
|
|
|
75
86
|
expect(processRPResult).toHaveBeenCalledWith(transactionId, paymentId, createdDate)
|
|
76
87
|
})
|
|
77
88
|
|
|
78
|
-
it('should validate with
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
|
|
89
|
+
it('should validate with processRPResultRequestParamsSchema', async () => {
|
|
90
|
+
const transactionId = Symbol('transaction-id')
|
|
91
|
+
const paymentId = Symbol('payment-id')
|
|
92
|
+
const createdDate = Symbol('created-date')
|
|
93
|
+
const request = getMockRequest({ transactionId, paymentId, createdDate })
|
|
94
|
+
await prpHandler(request, getMockResponseToolkit())
|
|
82
95
|
expect(recurringPayments[1].options.validate.params).toBe(processRPResultRequestParamsSchema)
|
|
83
96
|
})
|
|
84
97
|
})
|
|
98
|
+
|
|
99
|
+
describe('linkRecurringPayments', () => {
|
|
100
|
+
it('handler should return continue response', async () => {
|
|
101
|
+
const request = getMockRequest({})
|
|
102
|
+
const responseToolkit = getMockResponseToolkit()
|
|
103
|
+
expect(await lrpHandler(request, responseToolkit)).toEqual(responseToolkit.continue)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('should call linkRecurringPayments with existingRecurringPaymentId and agreementId', async () => {
|
|
107
|
+
const existingRecurringPaymentId = Symbol('existing-recurring-payment')
|
|
108
|
+
const agreementId = Symbol('agreement-id')
|
|
109
|
+
const request = getMockRequest({ existingRecurringPaymentId, agreementId })
|
|
110
|
+
await lrpHandler(request, getMockResponseToolkit())
|
|
111
|
+
expect(linkRecurringPayments).toHaveBeenCalledWith(existingRecurringPaymentId, agreementId)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('should validate with linkRecurringPaymentsRequestParamsSchema', async () => {
|
|
115
|
+
const existingRecurringPaymentId = Symbol('existing-recurring-payment')
|
|
116
|
+
const agreementId = Symbol('agreement-id')
|
|
117
|
+
const request = getMockRequest({ existingRecurringPaymentId, agreementId })
|
|
118
|
+
await lrpHandler(request, getMockResponseToolkit())
|
|
119
|
+
expect(recurringPayments[2].options.validate.params).toBe(linkRecurringPaymentsRequestParamsSchema)
|
|
120
|
+
})
|
|
121
|
+
})
|
|
85
122
|
})
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dueRecurringPaymentsRequestParamsSchema,
|
|
3
3
|
dueRecurringPaymentsResponseSchema,
|
|
4
|
+
linkRecurringPaymentsRequestParamsSchema,
|
|
4
5
|
processRPResultRequestParamsSchema
|
|
5
6
|
} from '../../schema/recurring-payments.schema.js'
|
|
6
|
-
import { getRecurringPayments, processRPResult } from '../../services/recurring-payments.service.js'
|
|
7
|
+
import { getRecurringPayments, linkRecurringPayments, processRPResult } from '../../services/recurring-payments.service.js'
|
|
8
|
+
|
|
9
|
+
const SWAGGER_TAGS = ['api', 'recurring-payments']
|
|
7
10
|
|
|
8
11
|
export default [
|
|
9
12
|
{
|
|
@@ -16,7 +19,7 @@ export default [
|
|
|
16
19
|
return h.response(result)
|
|
17
20
|
},
|
|
18
21
|
description: 'Retrieve recurring payments due for the specified date',
|
|
19
|
-
tags:
|
|
22
|
+
tags: SWAGGER_TAGS,
|
|
20
23
|
validate: {
|
|
21
24
|
params: dueRecurringPaymentsRequestParamsSchema
|
|
22
25
|
},
|
|
@@ -40,7 +43,7 @@ export default [
|
|
|
40
43
|
return h.response(result)
|
|
41
44
|
},
|
|
42
45
|
description: 'Generate a permission from a recurring payment record',
|
|
43
|
-
tags:
|
|
46
|
+
tags: SWAGGER_TAGS,
|
|
44
47
|
validate: {
|
|
45
48
|
params: processRPResultRequestParamsSchema
|
|
46
49
|
},
|
|
@@ -53,5 +56,29 @@ export default [
|
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
58
|
}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
method: 'GET',
|
|
62
|
+
path: '/linkRecurringPayments/{existingRecurringPaymentId}/{agreementId}',
|
|
63
|
+
options: {
|
|
64
|
+
handler: async (request, h) => {
|
|
65
|
+
const { existingRecurringPaymentId, agreementId } = request.params
|
|
66
|
+
const result = await linkRecurringPayments(existingRecurringPaymentId, agreementId)
|
|
67
|
+
return h.response(result)
|
|
68
|
+
},
|
|
69
|
+
description: 'Link an old RecurringPayment to its replacement',
|
|
70
|
+
tags: SWAGGER_TAGS,
|
|
71
|
+
validate: {
|
|
72
|
+
params: linkRecurringPaymentsRequestParamsSchema
|
|
73
|
+
},
|
|
74
|
+
plugins: {
|
|
75
|
+
'hapi-swagger': {
|
|
76
|
+
responses: {
|
|
77
|
+
200: { description: 'Old RecurringPayment linked to new RecurringPayment successfully' }
|
|
78
|
+
},
|
|
79
|
+
order: 3
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
56
83
|
}
|
|
57
84
|
]
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { findDueRecurringPayments, Permission } from '@defra-fish/dynamics-lib'
|
|
1
|
+
import { executeQuery, findDueRecurringPayments, findRecurringPaymentsByAgreementId, Permission } from '@defra-fish/dynamics-lib'
|
|
2
2
|
import {
|
|
3
3
|
getRecurringPayments,
|
|
4
4
|
processRecurringPayment,
|
|
5
5
|
generateRecurringPaymentRecord,
|
|
6
|
-
processRPResult
|
|
6
|
+
processRPResult,
|
|
7
|
+
linkRecurringPayments
|
|
7
8
|
} from '../recurring-payments.service.js'
|
|
8
9
|
import { calculateEndDate, generatePermissionNumber } from '../permissions.service.js'
|
|
9
10
|
import { getObfuscatedDob } from '../contacts.service.js'
|
|
@@ -20,7 +21,8 @@ jest.mock('@defra-fish/dynamics-lib', () => ({
|
|
|
20
21
|
...jest.requireActual('@defra-fish/dynamics-lib'),
|
|
21
22
|
executeQuery: jest.fn(),
|
|
22
23
|
findById: jest.fn(),
|
|
23
|
-
findDueRecurringPayments: jest.fn()
|
|
24
|
+
findDueRecurringPayments: jest.fn(),
|
|
25
|
+
findRecurringPaymentsByAgreementId: jest.fn(() => ['foo'])
|
|
24
26
|
}))
|
|
25
27
|
|
|
26
28
|
jest.mock('@defra-fish/connectors-lib', () => {
|
|
@@ -77,7 +79,7 @@ jest.mock('@defra-fish/business-rules-lib', () => ({
|
|
|
77
79
|
|
|
78
80
|
const dynamicsLib = jest.requireMock('@defra-fish/dynamics-lib')
|
|
79
81
|
|
|
80
|
-
const getMockRecurringPayment = () => ({
|
|
82
|
+
const getMockRecurringPayment = (overrides = {}) => ({
|
|
81
83
|
name: 'Test Name',
|
|
82
84
|
nextDueDate: '2019-12-14T00:00:00Z',
|
|
83
85
|
cancelledDate: null,
|
|
@@ -93,7 +95,8 @@ const getMockRecurringPayment = () => ({
|
|
|
93
95
|
activePermission: {
|
|
94
96
|
entity: getMockPermission()
|
|
95
97
|
}
|
|
96
|
-
}
|
|
98
|
+
},
|
|
99
|
+
...overrides
|
|
97
100
|
})
|
|
98
101
|
|
|
99
102
|
const getMockRPContactPermission = (contact, permission) => ({
|
|
@@ -619,4 +622,43 @@ describe('recurring payments service', () => {
|
|
|
619
622
|
})
|
|
620
623
|
})
|
|
621
624
|
})
|
|
625
|
+
|
|
626
|
+
describe('linkRecurringPayments', () => {
|
|
627
|
+
it('should call executeQuery with findRecurringPaymentsByAgreementId and the provided agreementId', async () => {
|
|
628
|
+
const agreementId = 'abc123'
|
|
629
|
+
await linkRecurringPayments('existing-recurring-payment-id', agreementId)
|
|
630
|
+
expect(executeQuery).toHaveBeenCalledWith(findRecurringPaymentsByAgreementId(agreementId))
|
|
631
|
+
})
|
|
632
|
+
|
|
633
|
+
it('should log a RecurringPayment record when there is one match', async () => {
|
|
634
|
+
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(jest.fn())
|
|
635
|
+
const onlyRecord = { entity: getMockRecurringPayment() }
|
|
636
|
+
dynamicsLib.executeQuery.mockReturnValueOnce([onlyRecord])
|
|
637
|
+
|
|
638
|
+
await linkRecurringPayments('existing-recurring-payment-id', 'agreement-id')
|
|
639
|
+
|
|
640
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('newRecurringPaymentId: ', onlyRecord.id)
|
|
641
|
+
})
|
|
642
|
+
|
|
643
|
+
it('should log the RecurringPayment record with the latest endDate when there are multiple matches', async () => {
|
|
644
|
+
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(jest.fn())
|
|
645
|
+
const oldestRecord = { entity: getMockRecurringPayment({ id: Symbol('oldest'), endDate: new Date('2021-12-15T00:00:00Z') }) }
|
|
646
|
+
const newestRecord = { entity: getMockRecurringPayment({ id: Symbol('newest'), endDate: new Date('2023-12-15T00:00:00Z') }) }
|
|
647
|
+
const middlestRecord = { entity: getMockRecurringPayment({ id: Symbol('middlest'), endDate: new Date('2022-12-15T00:00:00Z') }) }
|
|
648
|
+
dynamicsLib.executeQuery.mockReturnValueOnce([oldestRecord, newestRecord, middlestRecord])
|
|
649
|
+
|
|
650
|
+
await linkRecurringPayments('existing-recurring-payment-id', 'agreement-id')
|
|
651
|
+
|
|
652
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('newRecurringPaymentId: ', newestRecord.id)
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
it('should log no matches when there are no matches', async () => {
|
|
656
|
+
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(jest.fn())
|
|
657
|
+
dynamicsLib.executeQuery.mockReturnValueOnce([])
|
|
658
|
+
|
|
659
|
+
await linkRecurringPayments('existing-recurring-payment-id', 'agreement-id')
|
|
660
|
+
|
|
661
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('No matches found')
|
|
662
|
+
})
|
|
663
|
+
})
|
|
622
664
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { executeQuery, findDueRecurringPayments, RecurringPayment } from '@defra-fish/dynamics-lib'
|
|
1
|
+
import { executeQuery, findDueRecurringPayments, findRecurringPaymentsByAgreementId, RecurringPayment } from '@defra-fish/dynamics-lib'
|
|
2
2
|
import { calculateEndDate, generatePermissionNumber } from './permissions.service.js'
|
|
3
3
|
import { getObfuscatedDob } from './contacts.service.js'
|
|
4
4
|
import { createHash } from 'node:crypto'
|
|
@@ -126,3 +126,23 @@ export const processRPResult = async (transactionId, paymentId, createdDate) =>
|
|
|
126
126
|
|
|
127
127
|
return { permission }
|
|
128
128
|
}
|
|
129
|
+
|
|
130
|
+
export const linkRecurringPayments = async (existingRecurringPaymentId, agreementId) => {
|
|
131
|
+
console.log('existingRecurringPaymentId: ', existingRecurringPaymentId)
|
|
132
|
+
console.log('agreementId: ', agreementId)
|
|
133
|
+
const newRecurringPayment = await findNewestExistingRecurringPaymentInCrm(agreementId)
|
|
134
|
+
if (newRecurringPayment) {
|
|
135
|
+
console.log('newRecurringPaymentId: ', newRecurringPayment.id)
|
|
136
|
+
} else {
|
|
137
|
+
console.log('No matches found')
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const findNewestExistingRecurringPaymentInCrm = async agreementId => {
|
|
142
|
+
const matchingRecords = await executeQuery(findRecurringPaymentsByAgreementId(agreementId))
|
|
143
|
+
if (matchingRecords?.length) {
|
|
144
|
+
return [...matchingRecords].sort((a, b) => a.entity.endDate - b.entity.endDate).pop()
|
|
145
|
+
} else {
|
|
146
|
+
return false
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
FulfilmentRequest,
|
|
8
8
|
Permission,
|
|
9
9
|
PoclFile,
|
|
10
|
+
RecurringPayment,
|
|
10
11
|
RecurringPaymentInstruction,
|
|
11
12
|
Transaction,
|
|
12
|
-
TransactionJournal
|
|
13
|
-
RecurringPayment
|
|
13
|
+
TransactionJournal
|
|
14
14
|
} from '@defra-fish/dynamics-lib'
|
|
15
15
|
import {
|
|
16
16
|
mockFinalisedTransactionRecord,
|
|
@@ -65,7 +65,11 @@ jest.mock('@defra-fish/business-rules-lib', () => ({
|
|
|
65
65
|
START_AFTER_PAYMENT_MINUTES: 30
|
|
66
66
|
}))
|
|
67
67
|
|
|
68
|
-
jest.mock('../../recurring-payments.service.js')
|
|
68
|
+
jest.mock('../../recurring-payments.service.js', () => ({
|
|
69
|
+
findNewestExistingRecurringPaymentInCrm: jest.fn(),
|
|
70
|
+
generateRecurringPaymentRecord: jest.fn(),
|
|
71
|
+
processRecurringPayment: jest.fn()
|
|
72
|
+
}))
|
|
69
73
|
|
|
70
74
|
describe('transaction service', () => {
|
|
71
75
|
beforeAll(() => {
|