@defra-fish/sales-api-service 1.61.0-rc.1 → 1.61.0-rc.3

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/sales-api-service",
3
- "version": "1.61.0-rc.1",
3
+ "version": "1.61.0-rc.3",
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.1",
39
- "@defra-fish/connectors-lib": "1.61.0-rc.1",
40
- "@defra-fish/dynamics-lib": "1.61.0-rc.1",
38
+ "@defra-fish/business-rules-lib": "1.61.0-rc.3",
39
+ "@defra-fish/connectors-lib": "1.61.0-rc.3",
40
+ "@defra-fish/dynamics-lib": "1.61.0-rc.3",
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": "511d7de4ea42bfeca0078f5ff64b5e77c785d44b"
55
+ "gitHead": "49154a3c32f975fcf44b17a9a04f0c4e30f6e67d"
56
56
  }
@@ -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
+ findNewestExistingRecurringPaymentInCrm
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,34 @@ describe('recurring payments service', () => {
619
622
  })
620
623
  })
621
624
  })
625
+
626
+ describe('findNewestExistingRecurringPaymentInCrm', () => {
627
+ it('should call executeQuery with findRecurringPaymentsByAgreementId and the provided agreementId', async () => {
628
+ const agreementId = 'abc123'
629
+ await findNewestExistingRecurringPaymentInCrm(agreementId)
630
+ expect(executeQuery).toHaveBeenCalledWith(findRecurringPaymentsByAgreementId(agreementId))
631
+ })
632
+
633
+ it('should return a RecurringPayment record when there is one match', async () => {
634
+ const onlyRecord = getMockRecurringPayment()
635
+ dynamicsLib.executeQuery.mockReturnValueOnce([onlyRecord])
636
+ const returnedRecord = await findNewestExistingRecurringPaymentInCrm('agreementId')
637
+ expect(returnedRecord).toBe(onlyRecord)
638
+ })
639
+
640
+ it('should return the RecurringPayment record with the latest endDate when there are multiple matches', async () => {
641
+ const oldestRecord = getMockRecurringPayment({ endDate: '2021-12-15T00:00:00Z' })
642
+ const newestRecord = getMockRecurringPayment({ endDate: '2023-12-15T00:00:00Z' })
643
+ const middlestRecord = getMockRecurringPayment({ endDate: '2022-12-15T00:00:00Z' })
644
+ dynamicsLib.executeQuery.mockReturnValueOnce([oldestRecord, newestRecord, middlestRecord])
645
+ const returnedRecord = await findNewestExistingRecurringPaymentInCrm('agreementId')
646
+ expect(returnedRecord).toBe(newestRecord)
647
+ })
648
+
649
+ it('should return false when there are no matches', async () => {
650
+ dynamicsLib.executeQuery.mockReturnValueOnce([])
651
+ const returnedRecord = await findNewestExistingRecurringPaymentInCrm('agreementId')
652
+ expect(returnedRecord).toBe(false)
653
+ })
654
+ })
622
655
  })
@@ -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,12 @@ export const processRPResult = async (transactionId, paymentId, createdDate) =>
126
126
 
127
127
  return { permission }
128
128
  }
129
+
130
+ export const findNewestExistingRecurringPaymentInCrm = async agreementId => {
131
+ const matchingRecords = await executeQuery(findRecurringPaymentsByAgreementId(agreementId))
132
+ if (matchingRecords?.length) {
133
+ return [...matchingRecords].sort((a, b) => a.endDate.localeCompare(b.endDate)).pop()
134
+ } else {
135
+ return false
136
+ }
137
+ }
@@ -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,
@@ -26,7 +26,11 @@ import { TRANSACTION_STAGING_TABLE, TRANSACTION_STAGING_HISTORY_TABLE } from '..
26
26
  import AwsMock from 'aws-sdk'
27
27
  import { POCL_DATA_SOURCE, DDE_DATA_SOURCE } from '@defra-fish/business-rules-lib'
28
28
  import moment from 'moment'
29
- import { processRecurringPayment, generateRecurringPaymentRecord } from '../../recurring-payments.service.js'
29
+ import {
30
+ findNewestExistingRecurringPaymentInCrm,
31
+ processRecurringPayment,
32
+ generateRecurringPaymentRecord
33
+ } from '../../recurring-payments.service.js'
30
34
 
31
35
  jest.mock('../../reference-data.service.js', () => ({
32
36
  ...jest.requireActual('../../reference-data.service.js'),
@@ -65,7 +69,11 @@ jest.mock('@defra-fish/business-rules-lib', () => ({
65
69
  START_AFTER_PAYMENT_MINUTES: 30
66
70
  }))
67
71
 
68
- jest.mock('../../recurring-payments.service.js')
72
+ jest.mock('../../recurring-payments.service.js', () => ({
73
+ findNewestExistingRecurringPaymentInCrm: jest.fn(),
74
+ generateRecurringPaymentRecord: jest.fn(),
75
+ processRecurringPayment: jest.fn()
76
+ }))
69
77
 
70
78
  describe('transaction service', () => {
71
79
  beforeAll(() => {
@@ -410,6 +418,57 @@ describe('transaction service', () => {
410
418
  await processQueue({ id: finalisedTransaction.id })
411
419
  expect(processRecurringPayment).toHaveBeenCalledWith(rprSymbol, expect.any(Contact))
412
420
  })
421
+
422
+ it('checks for existing recurring payments with the same agreementId', async () => {
423
+ const agreementId = Symbol('agreementId')
424
+ const mockRecurringPayment = { agreementId }
425
+ const finalisedTransaction = mockFinalisedTransactionRecord()
426
+ processRecurringPayment.mockResolvedValueOnce({ recurringPayment: mockRecurringPayment })
427
+ AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: finalisedTransaction })
428
+
429
+ await processQueue({ id: finalisedTransaction.id })
430
+
431
+ expect(findNewestExistingRecurringPaymentInCrm).toHaveBeenCalledWith(agreementId)
432
+ })
433
+
434
+ it("assigns the new RecurringPayment as the existing RecurringPayment's nextRecurringPayment", async () => {
435
+ const agreementId = Symbol('agreementId')
436
+ const mockRecurringPayment = { agreementId }
437
+ const mockExistingRecurringPayment = { bindToEntity: jest.fn() }
438
+ const finalisedTransaction = mockFinalisedTransactionRecord()
439
+ processRecurringPayment.mockResolvedValueOnce({ recurringPayment: mockRecurringPayment })
440
+ AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: finalisedTransaction })
441
+ findNewestExistingRecurringPaymentInCrm.mockReturnValueOnce(mockExistingRecurringPayment)
442
+
443
+ await processQueue({ id: finalisedTransaction.id })
444
+
445
+ expect(mockExistingRecurringPayment.bindToEntity).toHaveBeenCalledWith(
446
+ RecurringPayment.definition.relationships.nextRecurringPayment,
447
+ mockRecurringPayment
448
+ )
449
+ })
450
+
451
+ it('does not attempt to update multiple recurring payment entities if no pre-existing recurring payment is found', async () => {
452
+ const agreementId = Symbol('agreementId')
453
+ const mockRecurringPayment = { agreementId }
454
+ const finalisedTransaction = mockFinalisedTransactionRecord()
455
+ processRecurringPayment.mockResolvedValueOnce({ recurringPayment: mockRecurringPayment })
456
+ AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: finalisedTransaction })
457
+ findNewestExistingRecurringPaymentInCrm.mockReturnValueOnce(false)
458
+
459
+ await processQueue({ id: finalisedTransaction.id })
460
+
461
+ expect(persist.mock.calls[0][0]).toEqual([
462
+ expect.objectContaining({ referenceNumber: finalisedTransaction.id }),
463
+ expect.objectContaining({ referenceNumber: finalisedTransaction.id }),
464
+ expect.objectContaining({ referenceNumber: finalisedTransaction.id }),
465
+ expect.objectContaining({ firstName: finalisedTransaction.permissions[0].licensee.firstName }),
466
+ expect.objectContaining({ referenceNumber: finalisedTransaction.permissions[0].referenceNumber }),
467
+ expect.objectContaining({ agreementId }),
468
+ expect.objectContaining({}),
469
+ expect.objectContaining({ referenceNumber: finalisedTransaction.permissions[0].concessions[0].proof.referenceNumber })
470
+ ])
471
+ })
413
472
  })
414
473
  })
415
474
 
@@ -8,11 +8,16 @@ import {
8
8
  Transaction,
9
9
  TransactionCurrency,
10
10
  TransactionJournal,
11
+ RecurringPayment,
11
12
  RecurringPaymentInstruction
12
13
  } from '@defra-fish/dynamics-lib'
13
14
  import { DDE_DATA_SOURCE, FULFILMENT_SWITCHOVER_DATE, POCL_TRANSACTION_SOURCES } from '@defra-fish/business-rules-lib'
14
15
  import { getReferenceDataForEntityAndId, getGlobalOptionSetValue, getReferenceDataForEntity } from '../reference-data.service.js'
15
- import { generateRecurringPaymentRecord, processRecurringPayment } from '../recurring-payments.service.js'
16
+ import {
17
+ findNewestExistingRecurringPaymentInCrm,
18
+ generateRecurringPaymentRecord,
19
+ processRecurringPayment
20
+ } from '../recurring-payments.service.js'
16
21
  import { resolveContactPayload } from '../contacts.service.js'
17
22
  import { retrieveStagedTransaction } from './retrieve-transaction.js'
18
23
  import { TRANSACTION_STAGING_TABLE, TRANSACTION_STAGING_HISTORY_TABLE } from '../../config.js'
@@ -82,6 +87,12 @@ export async function processQueue ({ id }) {
82
87
  paymentInstruction.bindToEntity(RecurringPaymentInstruction.definition.relationships.permit, permit)
83
88
  paymentInstruction.bindToEntity(RecurringPaymentInstruction.definition.relationships.recurringPayment, recurringPayment)
84
89
  entities.push(paymentInstruction)
90
+
91
+ const existingRecurringPayment = await findNewestExistingRecurringPaymentInCrm(recurringPayment.agreementId)
92
+ if (existingRecurringPayment) {
93
+ existingRecurringPayment.bindToEntity(RecurringPayment.definition.relationships.nextRecurringPayment, recurringPayment)
94
+ entities.push(existingRecurringPayment)
95
+ }
85
96
  }
86
97
 
87
98
  for (const concession of concessions || []) {