@defra-fish/sales-api-service 1.61.0-rc.16 → 1.61.0-rc.17
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 +1 -28
- package/src/schema/recurring-payments.schema.js +0 -5
- package/src/server/routes/__tests__/recurring-payments.spec.js +4 -37
- package/src/server/routes/recurring-payments.js +1 -26
- package/src/services/__tests__/recurring-payments.service.spec.js +117 -38
- package/src/services/recurring-payments.service.js +14 -18
- package/src/services/transactions/__tests__/process-transaction-queue.spec.js +53 -10
- package/src/services/transactions/process-transaction-queue.js +11 -7
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.17",
|
|
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.17",
|
|
39
|
+
"@defra-fish/connectors-lib": "1.61.0-rc.17",
|
|
40
|
+
"@defra-fish/dynamics-lib": "1.61.0-rc.17",
|
|
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": "e35e018372742acfe0cb41e31abd14d3b0c7f742"
|
|
56
56
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dueRecurringPaymentsRequestParamsSchema,
|
|
3
3
|
dueRecurringPaymentsResponseSchema,
|
|
4
|
-
processRPResultRequestParamsSchema
|
|
5
|
-
linkRecurringPaymentsRequestParamsSchema
|
|
4
|
+
processRPResultRequestParamsSchema
|
|
6
5
|
} from '../recurring-payments.schema.js'
|
|
7
6
|
|
|
8
7
|
jest.mock('../validators/validators.js', () => ({
|
|
@@ -63,11 +62,6 @@ const getProcessRPResultSampleData = () => ({
|
|
|
63
62
|
createdDate: '2025-01-01T00:00:00.000Z'
|
|
64
63
|
})
|
|
65
64
|
|
|
66
|
-
const getLinkRecurringPaymentsSampleData = () => ({
|
|
67
|
-
existingRecurringPaymentId: 'ghi123',
|
|
68
|
-
agreementId: 'jkl456'
|
|
69
|
-
})
|
|
70
|
-
|
|
71
65
|
describe('getDueRecurringPaymentsSchema', () => {
|
|
72
66
|
it('validates expected object', async () => {
|
|
73
67
|
expect(() => dueRecurringPaymentsResponseSchema.validateAsync(getResponseSampleData())).not.toThrow()
|
|
@@ -155,24 +149,3 @@ describe('processRPResultRequestParamsSchema', () => {
|
|
|
155
149
|
expect(() => processRPResultRequestParamsSchema.validateAsync(sampleData).rejects.toThrow())
|
|
156
150
|
})
|
|
157
151
|
})
|
|
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,8 +29,3 @@ 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,10 +1,6 @@
|
|
|
1
1
|
import recurringPayments from '../recurring-payments.js'
|
|
2
|
-
import { getRecurringPayments, processRPResult
|
|
3
|
-
import {
|
|
4
|
-
dueRecurringPaymentsRequestParamsSchema,
|
|
5
|
-
processRPResultRequestParamsSchema,
|
|
6
|
-
linkRecurringPaymentsRequestParamsSchema
|
|
7
|
-
} from '../../../schema/recurring-payments.schema.js'
|
|
2
|
+
import { getRecurringPayments, processRPResult } from '../../../services/recurring-payments.service.js'
|
|
3
|
+
import { dueRecurringPaymentsRequestParamsSchema, processRPResultRequestParamsSchema } from '../../../schema/recurring-payments.schema.js'
|
|
8
4
|
|
|
9
5
|
const [
|
|
10
6
|
{
|
|
@@ -12,22 +8,17 @@ const [
|
|
|
12
8
|
},
|
|
13
9
|
{
|
|
14
10
|
options: { handler: prpHandler }
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
options: { handler: lrpHandler }
|
|
18
11
|
}
|
|
19
12
|
] = recurringPayments
|
|
20
13
|
|
|
21
14
|
jest.mock('../../../services/recurring-payments.service.js', () => ({
|
|
22
15
|
getRecurringPayments: jest.fn(),
|
|
23
|
-
processRPResult: jest.fn()
|
|
24
|
-
linkRecurringPayments: jest.fn()
|
|
16
|
+
processRPResult: jest.fn()
|
|
25
17
|
}))
|
|
26
18
|
|
|
27
19
|
jest.mock('../../../schema/recurring-payments.schema.js', () => ({
|
|
28
20
|
dueRecurringPaymentsRequestParamsSchema: jest.fn(),
|
|
29
|
-
processRPResultRequestParamsSchema: jest.fn()
|
|
30
|
-
linkRecurringPaymentsRequestParamsSchema: jest.fn()
|
|
21
|
+
processRPResultRequestParamsSchema: jest.fn()
|
|
31
22
|
}))
|
|
32
23
|
|
|
33
24
|
const getMockRequest = ({
|
|
@@ -95,28 +86,4 @@ describe('recurring payments', () => {
|
|
|
95
86
|
expect(recurringPayments[1].options.validate.params).toBe(processRPResultRequestParamsSchema)
|
|
96
87
|
})
|
|
97
88
|
})
|
|
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
|
-
})
|
|
122
89
|
})
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dueRecurringPaymentsRequestParamsSchema,
|
|
3
3
|
dueRecurringPaymentsResponseSchema,
|
|
4
|
-
linkRecurringPaymentsRequestParamsSchema,
|
|
5
4
|
processRPResultRequestParamsSchema
|
|
6
5
|
} from '../../schema/recurring-payments.schema.js'
|
|
7
|
-
import { getRecurringPayments,
|
|
6
|
+
import { getRecurringPayments, processRPResult } from '../../services/recurring-payments.service.js'
|
|
8
7
|
|
|
9
8
|
const SWAGGER_TAGS = ['api', 'recurring-payments']
|
|
10
9
|
|
|
@@ -56,29 +55,5 @@ export default [
|
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
}
|
|
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
|
-
}
|
|
83
58
|
}
|
|
84
59
|
]
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
dynamicsClient,
|
|
3
|
+
executeQuery,
|
|
4
|
+
findDueRecurringPayments,
|
|
5
|
+
findRecurringPaymentsByAgreementId,
|
|
6
|
+
Permission,
|
|
7
|
+
RecurringPayment
|
|
8
|
+
} from '@defra-fish/dynamics-lib'
|
|
2
9
|
import {
|
|
3
10
|
getRecurringPayments,
|
|
4
11
|
processRecurringPayment,
|
|
5
12
|
generateRecurringPaymentRecord,
|
|
6
13
|
processRPResult,
|
|
7
|
-
|
|
14
|
+
findNewestExistingRecurringPaymentInCrm
|
|
8
15
|
} from '../recurring-payments.service.js'
|
|
9
16
|
import { calculateEndDate, generatePermissionNumber } from '../permissions.service.js'
|
|
10
17
|
import { getObfuscatedDob } from '../contacts.service.js'
|
|
@@ -20,9 +27,11 @@ const { docClient, sqs } = AWS.mock.results[0].value
|
|
|
20
27
|
jest.mock('@defra-fish/dynamics-lib', () => ({
|
|
21
28
|
...jest.requireActual('@defra-fish/dynamics-lib'),
|
|
22
29
|
executeQuery: jest.fn(),
|
|
23
|
-
findById: jest.fn(),
|
|
24
30
|
findDueRecurringPayments: jest.fn(),
|
|
25
|
-
findRecurringPaymentsByAgreementId: jest.fn(() =>
|
|
31
|
+
findRecurringPaymentsByAgreementId: jest.fn(() => ({ toRetrieveRequest: () => {} })),
|
|
32
|
+
dynamicsClient: {
|
|
33
|
+
retrieveMultipleRequest: jest.fn(() => ({ value: [] }))
|
|
34
|
+
}
|
|
26
35
|
}))
|
|
27
36
|
|
|
28
37
|
jest.mock('@defra-fish/connectors-lib', () => ({
|
|
@@ -80,8 +89,6 @@ jest.mock('@defra-fish/business-rules-lib', () => ({
|
|
|
80
89
|
}
|
|
81
90
|
}))
|
|
82
91
|
|
|
83
|
-
const dynamicsLib = jest.requireMock('@defra-fish/dynamics-lib')
|
|
84
|
-
|
|
85
92
|
const getMockRecurringPayment = (overrides = {}) => ({
|
|
86
93
|
name: 'Test Name',
|
|
87
94
|
nextDueDate: '2019-12-14T00:00:00Z',
|
|
@@ -168,6 +175,54 @@ const getMockTransaction = (id = 'test-id') => ({
|
|
|
168
175
|
]
|
|
169
176
|
})
|
|
170
177
|
|
|
178
|
+
const getMockResponse = () => ({
|
|
179
|
+
'@odata.context': 'https://ea-fish-devsandbox.crm4.dynamics.com/api/data/v9.1/$metadata#defra_recurringpayments',
|
|
180
|
+
value: [
|
|
181
|
+
{
|
|
182
|
+
'@odata.etag': 'W/"183695502"',
|
|
183
|
+
defra_recurringpaymentid: 'ed8e5dc7-d346-f011-877a-6045bde009c2',
|
|
184
|
+
defra_name: null,
|
|
185
|
+
statecode: 1,
|
|
186
|
+
defra_nextduedate: '2025-06-11T00:00:00Z',
|
|
187
|
+
defra_cancelleddate: null,
|
|
188
|
+
defra_cancelledreason: null,
|
|
189
|
+
defra_enddate: '2025-06-20T22:59:59Z',
|
|
190
|
+
defra_agreementid: 's6i8q2lcrgqene2s8u1qeaiel6',
|
|
191
|
+
defra_publicid: 'JIwepr5/XgxwLHF9u20F1veSdWqeMJz/dzVfjPKrflM=',
|
|
192
|
+
defra_lastdigitscardnumbers: null
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
'@odata.etag': 'W/"184173292"',
|
|
196
|
+
statecode: 1,
|
|
197
|
+
defra_recurringpaymentid: 'f5011f95-ac47-f011-877a-6045bde009c2',
|
|
198
|
+
defra_publicid: '/KTtr4kFQvLwSP3Xz/xAxh9jWbGuzW17ALni4rmip4k=',
|
|
199
|
+
defra_cancelleddate: null,
|
|
200
|
+
defra_inceptionmonth: null,
|
|
201
|
+
defra_cancelledreason: null,
|
|
202
|
+
defra_nextduedate: '2026-06-10T00:00:00Z',
|
|
203
|
+
defra_last_digits_card_number: null,
|
|
204
|
+
defra_enddate: '2026-06-20T22:59:59Z',
|
|
205
|
+
defra_agreementid: 's6i8q2lcrgqene2s8u1qeaiel6',
|
|
206
|
+
defra_lastdigitscardnumbers: null,
|
|
207
|
+
defra_name: null
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
'@odata.etag': 'W/"184173352"',
|
|
211
|
+
statecode: 1,
|
|
212
|
+
defra_recurringpaymentid: '0d021f95-ac47-f011-877a-6045bde009c2',
|
|
213
|
+
defra_publicid: 'y3vgHMkqiMyp0vyb3rrS9KvqvU+kUl6P4b74X62SFOU=',
|
|
214
|
+
defra_cancelleddate: null,
|
|
215
|
+
defra_cancelledreason: null,
|
|
216
|
+
defra_nextduedate: '2024-06-10T00:00:00Z',
|
|
217
|
+
defra_last_digits_card_number: null,
|
|
218
|
+
defra_enddate: '2024-06-20T22:59:59Z',
|
|
219
|
+
defra_agreementid: 's6i8q2lcrgqene2s8u1qeaiel6',
|
|
220
|
+
defra_lastdigitscardnumbers: null,
|
|
221
|
+
defra_name: null
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
})
|
|
225
|
+
|
|
171
226
|
describe('recurring payments service', () => {
|
|
172
227
|
const createSimpleSampleTransactionRecord = () => ({ payment: { recurring: true }, permissions: [{}] })
|
|
173
228
|
const createSamplePermission = overrides => {
|
|
@@ -197,7 +252,7 @@ describe('recurring payments service', () => {
|
|
|
197
252
|
const mockContact = mockRecurringPayments[0].expanded.contact
|
|
198
253
|
const mockPermission = mockRecurringPayments[0].expanded.activePermission
|
|
199
254
|
|
|
200
|
-
|
|
255
|
+
executeQuery.mockResolvedValueOnce(mockRecurringPayments)
|
|
201
256
|
|
|
202
257
|
const result = await getRecurringPayments(new Date())
|
|
203
258
|
|
|
@@ -207,11 +262,11 @@ describe('recurring payments service', () => {
|
|
|
207
262
|
it('executeQuery is called with findDueRecurringPayments with a date', async () => {
|
|
208
263
|
const mockRecurringPayments = [getMockRecurringPayment()]
|
|
209
264
|
const mockDate = new Date()
|
|
210
|
-
|
|
265
|
+
executeQuery.mockResolvedValueOnce(mockRecurringPayments)
|
|
211
266
|
|
|
212
267
|
await getRecurringPayments(mockDate)
|
|
213
268
|
|
|
214
|
-
expect(
|
|
269
|
+
expect(executeQuery).toHaveBeenCalledWith(findDueRecurringPayments(mockDate))
|
|
215
270
|
})
|
|
216
271
|
})
|
|
217
272
|
|
|
@@ -616,42 +671,66 @@ describe('recurring payments service', () => {
|
|
|
616
671
|
})
|
|
617
672
|
})
|
|
618
673
|
|
|
619
|
-
describe('
|
|
620
|
-
it('
|
|
621
|
-
const agreementId = '
|
|
622
|
-
await
|
|
623
|
-
expect(
|
|
674
|
+
describe('findNewestExistingRecurringPaymentInCrm', () => {
|
|
675
|
+
it('passes agreement id to findRecurringPaymentsByAgreementId', async () => {
|
|
676
|
+
const agreementId = Symbol('agreement-id')
|
|
677
|
+
await findNewestExistingRecurringPaymentInCrm(agreementId)
|
|
678
|
+
expect(findRecurringPaymentsByAgreementId).toHaveBeenCalledWith(agreementId)
|
|
624
679
|
})
|
|
625
680
|
|
|
626
|
-
it('
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
await linkRecurringPayments('existing-recurring-payment-id', 'agreement-id')
|
|
632
|
-
|
|
633
|
-
expect(consoleLogSpy).toHaveBeenCalledWith('newRecurringPaymentId: ', onlyRecord.id)
|
|
681
|
+
it('passes query created by findRecurringPaymentsByAgreementId to retrieveMultipleRequest', async () => {
|
|
682
|
+
const retrieveRequest = Symbol('retrieve request')
|
|
683
|
+
findRecurringPaymentsByAgreementId.mockReturnValueOnce({ toRetrieveRequest: () => retrieveRequest })
|
|
684
|
+
await findNewestExistingRecurringPaymentInCrm()
|
|
685
|
+
expect(dynamicsClient.retrieveMultipleRequest).toHaveBeenCalledWith(retrieveRequest)
|
|
634
686
|
})
|
|
635
687
|
|
|
636
|
-
it('
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
dynamicsLib.executeQuery.mockReturnValueOnce([oldestRecord, newestRecord, middlestRecord])
|
|
642
|
-
|
|
643
|
-
await linkRecurringPayments('existing-recurring-payment-id', 'agreement-id')
|
|
644
|
-
|
|
645
|
-
expect(consoleLogSpy).toHaveBeenCalledWith('newRecurringPaymentId: ', newestRecord.id)
|
|
688
|
+
it('returns a Recurring Payment (not a plain object)', async () => {
|
|
689
|
+
jest.spyOn(RecurringPayment, 'fromResponse')
|
|
690
|
+
dynamicsClient.retrieveMultipleRequest.mockReturnValueOnce(getMockResponse())
|
|
691
|
+
const rcp = await findNewestExistingRecurringPaymentInCrm()
|
|
692
|
+
expect(RecurringPayment.fromResponse.mock.results[0].value).toBe(rcp)
|
|
646
693
|
})
|
|
647
694
|
|
|
648
|
-
it(
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
695
|
+
it.each([
|
|
696
|
+
[
|
|
697
|
+
'dataset of three with midpoint most recent',
|
|
698
|
+
[
|
|
699
|
+
{ defra_recurringpaymentid: '1', defra_enddate: '2024-05-31T00:37:24.123' },
|
|
700
|
+
{ defra_recurringpaymentid: '2', defra_enddate: '2025-05-31T00:37:24.456' },
|
|
701
|
+
{ defra_recurringpaymentid: '3', defra_enddate: '2025-05-31T00:37:24.123' }
|
|
702
|
+
],
|
|
703
|
+
'2'
|
|
704
|
+
],
|
|
705
|
+
[
|
|
706
|
+
'dataset of five with penultimate most recent',
|
|
707
|
+
[
|
|
708
|
+
{ defra_recurringpaymentid: '111-aaa', defra_enddate: '2024-05-31T00:37:24.123' },
|
|
709
|
+
{ defra_recurringpaymentid: '222-ddd', defra_enddate: '2022-05-31T00:37:24.456' },
|
|
710
|
+
{ defra_recurringpaymentid: '304-jkj', defra_enddate: '2021-05-31T00:37:24.123' },
|
|
711
|
+
{ defra_recurringpaymentid: 'abc-123', defra_enddate: '2025-05-31T00:37:24.123' },
|
|
712
|
+
{ defra_recurringpaymentid: '908-oid', defra_enddate: '1876-05-30T00:37:24.123' }
|
|
713
|
+
],
|
|
714
|
+
'abc-123'
|
|
715
|
+
],
|
|
716
|
+
[
|
|
717
|
+
'dataset of two with last most recent',
|
|
718
|
+
[
|
|
719
|
+
{ defra_recurringpaymentid: 'abc-123', defra_enddate: '1876-05-30T00:37:24.123' },
|
|
720
|
+
{ defra_recurringpaymentid: '908-oid', defra_enddate: '2025-05-31T00:37:24.123' }
|
|
721
|
+
],
|
|
722
|
+
'908-oid'
|
|
723
|
+
]
|
|
724
|
+
])('returns most recent existing recurring payment from %s', async (_desc, mockResponseData, expectedId) => {
|
|
725
|
+
dynamicsClient.retrieveMultipleRequest.mockReturnValueOnce({ value: mockResponseData })
|
|
726
|
+
const rcp = await findNewestExistingRecurringPaymentInCrm()
|
|
727
|
+
expect(rcp.id).toBe(expectedId)
|
|
728
|
+
})
|
|
653
729
|
|
|
654
|
-
|
|
730
|
+
it('returns boolean false if no recurring payments found', async () => {
|
|
731
|
+
dynamicsClient.retrieveMultipleRequest.mockReturnValueOnce({ value: [] })
|
|
732
|
+
const rcp = await findNewestExistingRecurringPaymentInCrm()
|
|
733
|
+
expect(rcp).toBeFalsy()
|
|
655
734
|
})
|
|
656
735
|
})
|
|
657
736
|
})
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
executeQuery,
|
|
3
|
+
findDueRecurringPayments,
|
|
4
|
+
findRecurringPaymentsByAgreementId,
|
|
5
|
+
RecurringPayment,
|
|
6
|
+
dynamicsClient
|
|
7
|
+
} from '@defra-fish/dynamics-lib'
|
|
2
8
|
import { calculateEndDate, generatePermissionNumber } from './permissions.service.js'
|
|
3
9
|
import { getObfuscatedDob } from './contacts.service.js'
|
|
4
10
|
import { createHash } from 'node:crypto'
|
|
@@ -123,22 +129,12 @@ export const processRPResult = async (transactionId, paymentId, createdDate) =>
|
|
|
123
129
|
return { permission }
|
|
124
130
|
}
|
|
125
131
|
|
|
126
|
-
export const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
} else {
|
|
133
|
-
console.log('No matches found')
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const findNewestExistingRecurringPaymentInCrm = async agreementId => {
|
|
138
|
-
const matchingRecords = await executeQuery(findRecurringPaymentsByAgreementId(agreementId))
|
|
139
|
-
if (matchingRecords?.length) {
|
|
140
|
-
return [...matchingRecords].sort((a, b) => a.entity.endDate - b.entity.endDate).pop()
|
|
141
|
-
} else {
|
|
142
|
-
return false
|
|
132
|
+
export const findNewestExistingRecurringPaymentInCrm = async agreementId => {
|
|
133
|
+
const query = findRecurringPaymentsByAgreementId(agreementId)
|
|
134
|
+
const response = await dynamicsClient.retrieveMultipleRequest(query.toRetrieveRequest())
|
|
135
|
+
if (response.value.length) {
|
|
136
|
+
const [rcpResponseData] = response.value.sort((a, b) => Date.parse(b.defra_enddate) - Date.parse(a.defra_enddate))
|
|
137
|
+
return RecurringPayment.fromResponse(rcpResponseData)
|
|
143
138
|
}
|
|
139
|
+
return false
|
|
144
140
|
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { processQueue, getTransactionJournalRefNumber } from '../process-transaction-queue.js'
|
|
2
2
|
import {
|
|
3
3
|
persist,
|
|
4
|
-
findById,
|
|
5
4
|
ConcessionProof,
|
|
6
5
|
Contact,
|
|
7
6
|
FulfilmentRequest,
|
|
8
7
|
Permission,
|
|
9
|
-
PoclFile,
|
|
10
8
|
RecurringPayment,
|
|
11
|
-
RecurringPaymentInstruction,
|
|
12
9
|
Transaction,
|
|
13
10
|
TransactionJournal
|
|
14
11
|
} from '@defra-fish/dynamics-lib'
|
|
@@ -25,7 +22,11 @@ import {
|
|
|
25
22
|
import { TRANSACTION_STAGING_TABLE, TRANSACTION_STAGING_HISTORY_TABLE } from '../../../config.js'
|
|
26
23
|
import { POCL_DATA_SOURCE, DDE_DATA_SOURCE } from '@defra-fish/business-rules-lib'
|
|
27
24
|
import moment from 'moment'
|
|
28
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
processRecurringPayment,
|
|
27
|
+
generateRecurringPaymentRecord,
|
|
28
|
+
findNewestExistingRecurringPaymentInCrm
|
|
29
|
+
} from '../../recurring-payments.service.js'
|
|
29
30
|
import { AWS } from '@defra-fish/connectors-lib'
|
|
30
31
|
const { docClient } = AWS.mock.results[0].value
|
|
31
32
|
|
|
@@ -50,8 +51,7 @@ jest.mock('../../reference-data.service.js', () => ({
|
|
|
50
51
|
|
|
51
52
|
jest.mock('@defra-fish/dynamics-lib', () => ({
|
|
52
53
|
...jest.requireActual('@defra-fish/dynamics-lib'),
|
|
53
|
-
persist: jest.fn()
|
|
54
|
-
findById: jest.fn()
|
|
54
|
+
persist: jest.fn()
|
|
55
55
|
}))
|
|
56
56
|
|
|
57
57
|
jest.mock('../../contacts.service.js', () => ({
|
|
@@ -169,7 +169,36 @@ describe('transaction service', () => {
|
|
|
169
169
|
expect.any(Contact),
|
|
170
170
|
expect.any(Permission),
|
|
171
171
|
expect.any(RecurringPayment),
|
|
172
|
-
expect.any(
|
|
172
|
+
expect.any(ConcessionProof)
|
|
173
|
+
]
|
|
174
|
+
],
|
|
175
|
+
[
|
|
176
|
+
'licences with a previous recurring payment linked to new recurring payment',
|
|
177
|
+
() => {
|
|
178
|
+
processRecurringPayment.mockResolvedValueOnce({ recurringPayment: new RecurringPayment() })
|
|
179
|
+
findNewestExistingRecurringPaymentInCrm.mockResolvedValueOnce(new RecurringPayment())
|
|
180
|
+
const mockRecord = mockFinalisedTransactionRecord()
|
|
181
|
+
mockRecord.payment.recurring = {
|
|
182
|
+
name: 'Test name',
|
|
183
|
+
nextDueDate: new Date('2020/01/11'),
|
|
184
|
+
endDate: new Date('2022/01/16'),
|
|
185
|
+
agreementId: '123446jjng',
|
|
186
|
+
publicId: 'sdf-123',
|
|
187
|
+
status: 1,
|
|
188
|
+
activePermission: mockRecord.permissions[0],
|
|
189
|
+
contact: Object.assign(mockContactPayload(), { firstName: 'Esther' })
|
|
190
|
+
}
|
|
191
|
+
mockRecord.permissions[0].permitId = MOCK_12MONTH_SENIOR_PERMIT.id
|
|
192
|
+
return mockRecord
|
|
193
|
+
},
|
|
194
|
+
[
|
|
195
|
+
expect.any(Transaction),
|
|
196
|
+
expect.any(TransactionJournal),
|
|
197
|
+
expect.any(TransactionJournal),
|
|
198
|
+
expect.any(Contact),
|
|
199
|
+
expect.any(Permission),
|
|
200
|
+
expect.any(RecurringPayment),
|
|
201
|
+
expect.any(RecurringPayment),
|
|
173
202
|
expect.any(ConcessionProof)
|
|
174
203
|
]
|
|
175
204
|
]
|
|
@@ -338,15 +367,13 @@ describe('transaction service', () => {
|
|
|
338
367
|
expect(persistMockFirstAgument[0][4].isLicenceForYou).toBeUndefined()
|
|
339
368
|
})
|
|
340
369
|
|
|
341
|
-
it('handles requests which relate to
|
|
370
|
+
it('handles requests which relate to a transaction file', async () => {
|
|
342
371
|
const transactionFilename = 'test-file.xml'
|
|
343
372
|
const mockRecord = mockFinalisedTransactionRecord()
|
|
344
373
|
mockRecord.transactionFile = transactionFilename
|
|
345
374
|
docClient.get.mockResolvedValueOnce({ Item: mockRecord })
|
|
346
375
|
const transactionToFileBindingSpy = jest.spyOn(Transaction.prototype, 'bindToAlternateKey')
|
|
347
376
|
const permissionToFileBindingSpy = jest.spyOn(Permission.prototype, 'bindToAlternateKey')
|
|
348
|
-
const testPoclFileEntity = new PoclFile()
|
|
349
|
-
findById.mockResolvedValueOnce(testPoclFileEntity)
|
|
350
377
|
await processQueue({ id: mockRecord.id })
|
|
351
378
|
expect(transactionToFileBindingSpy).toHaveBeenCalledWith(Transaction.definition.relationships.poclFile, transactionFilename)
|
|
352
379
|
expect(permissionToFileBindingSpy).toHaveBeenCalledWith(Permission.definition.relationships.poclFile, transactionFilename)
|
|
@@ -428,6 +455,22 @@ describe('transaction service', () => {
|
|
|
428
455
|
await processQueue({ id: finalisedTransaction.id })
|
|
429
456
|
expect(processRecurringPayment).toHaveBeenCalledWith(rprSymbol, expect.any(Contact))
|
|
430
457
|
})
|
|
458
|
+
|
|
459
|
+
it('binds existing recurring payment to new recurring payment', async () => {
|
|
460
|
+
const mockExistingRecurringPayment = {
|
|
461
|
+
bindToEntity: jest.fn()
|
|
462
|
+
}
|
|
463
|
+
const mockNewRecurringPayment = new RecurringPayment()
|
|
464
|
+
const finalisedTransaction = mockFinalisedTransactionRecord()
|
|
465
|
+
findNewestExistingRecurringPaymentInCrm.mockReturnValueOnce(mockExistingRecurringPayment)
|
|
466
|
+
processRecurringPayment.mockReturnValueOnce({ recurringPayment: mockNewRecurringPayment })
|
|
467
|
+
docClient.get.mockResolvedValueOnce({ Item: finalisedTransaction })
|
|
468
|
+
await processQueue({ id: finalisedTransaction.id })
|
|
469
|
+
expect(mockExistingRecurringPayment.bindToEntity).toHaveBeenCalledWith(
|
|
470
|
+
RecurringPayment.definition.relationships.nextRecurringPayment,
|
|
471
|
+
mockNewRecurringPayment
|
|
472
|
+
)
|
|
473
|
+
})
|
|
431
474
|
})
|
|
432
475
|
})
|
|
433
476
|
|
|
@@ -8,11 +8,15 @@ import {
|
|
|
8
8
|
Transaction,
|
|
9
9
|
TransactionCurrency,
|
|
10
10
|
TransactionJournal,
|
|
11
|
-
|
|
11
|
+
RecurringPayment
|
|
12
12
|
} from '@defra-fish/dynamics-lib'
|
|
13
13
|
import { DDE_DATA_SOURCE, FULFILMENT_SWITCHOVER_DATE, POCL_TRANSACTION_SOURCES } from '@defra-fish/business-rules-lib'
|
|
14
14
|
import { getReferenceDataForEntityAndId, getGlobalOptionSetValue, getReferenceDataForEntity } from '../reference-data.service.js'
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
generateRecurringPaymentRecord,
|
|
17
|
+
processRecurringPayment,
|
|
18
|
+
findNewestExistingRecurringPaymentInCrm
|
|
19
|
+
} from '../recurring-payments.service.js'
|
|
16
20
|
import { resolveContactPayload } from '../contacts.service.js'
|
|
17
21
|
import { retrieveStagedTransaction } from './retrieve-transaction.js'
|
|
18
22
|
import { TRANSACTION_STAGING_TABLE, TRANSACTION_STAGING_HISTORY_TABLE } from '../../config.js'
|
|
@@ -77,11 +81,11 @@ export async function processQueue ({ id }) {
|
|
|
77
81
|
|
|
78
82
|
if (recurringPayment && permit.isRecurringPaymentSupported) {
|
|
79
83
|
entities.push(recurringPayment)
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
const existingRP = await findNewestExistingRecurringPaymentInCrm(recurringPayment.agreementId)
|
|
85
|
+
if (existingRP) {
|
|
86
|
+
existingRP.bindToEntity(RecurringPayment.definition.relationships.nextRecurringPayment, recurringPayment)
|
|
87
|
+
entities.push(existingRP)
|
|
88
|
+
}
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
for (const concession of concessions || []) {
|