@defra-fish/sales-api-service 1.77.0-rc.11 → 1.77.0-rc.12

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.77.0-rc.11",
3
+ "version": "1.77.0-rc.12",
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.77.0-rc.11",
39
- "@defra-fish/connectors-lib": "1.77.0-rc.11",
40
- "@defra-fish/dynamics-lib": "1.77.0-rc.11",
38
+ "@defra-fish/business-rules-lib": "1.77.0-rc.12",
39
+ "@defra-fish/connectors-lib": "1.77.0-rc.12",
40
+ "@defra-fish/dynamics-lib": "1.77.0-rc.12",
41
41
  "@hapi/boom": "9.1.2",
42
42
  "@hapi/hapi": "20.1.3",
43
43
  "@hapi/inert": "6.0.3",
@@ -53,5 +53,5 @@
53
53
  "moment-timezone": "0.5.34",
54
54
  "uuid": "8.3.2"
55
55
  },
56
- "gitHead": "df46c8202b593c1b69d7d0c1c83d344f410d1654"
56
+ "gitHead": "f85b4e4072fbf9922dfaf75347b9c794c719c0b9"
57
57
  }
@@ -1,3 +1,4 @@
1
+ import db from 'debug'
1
2
  import { processQueue, getTransactionJournalRefNumber } from '../process-transaction-queue.js'
2
3
  import {
3
4
  persist,
@@ -27,8 +28,12 @@ import {
27
28
  generateRecurringPaymentRecord,
28
29
  findNewestExistingRecurringPaymentInCrm
29
30
  } from '../../recurring-payments.service.js'
31
+ import { retrieveStagedTransaction } from '../retrieve-transaction.js'
30
32
  import { AWS } from '@defra-fish/connectors-lib'
31
33
  const { docClient } = AWS.mock.results[0].value
34
+ const { value: debug } = db.mock.results[db.mock.calls.findIndex(c => c[0] === 'sales:transactions')]
35
+
36
+ jest.mock('debug', () => jest.fn(() => jest.fn()))
32
37
 
33
38
  jest.mock('../../reference-data.service.js', () => ({
34
39
  ...jest.requireActual('../../reference-data.service.js'),
@@ -72,6 +77,10 @@ jest.mock('../../recurring-payments.service.js', () => ({
72
77
  processRecurringPayment: jest.fn()
73
78
  }))
74
79
 
80
+ jest.mock('../retrieve-transaction.js', () => ({
81
+ retrieveStagedTransaction: jest.fn()
82
+ }))
83
+
75
84
  jest.mock('@defra-fish/connectors-lib', () => {
76
85
  const mockAWS = {
77
86
  docClient: {
@@ -204,17 +213,11 @@ describe('transaction service', () => {
204
213
  ]
205
214
  ])('handles %s', async (description, initialiseMockTransactionRecord, entityExpectations) => {
206
215
  const mockRecord = initialiseMockTransactionRecord()
207
- docClient.get.mockResolvedValueOnce({ Item: mockRecord })
216
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
208
217
  const result = await processQueue({ id: mockRecord.id })
209
218
  expect(result).toBeUndefined()
210
219
  expect(persist).toBeCalledWith(entityExpectations, undefined)
211
- expect(docClient.get).toHaveBeenCalledWith(
212
- expect.objectContaining({
213
- TableName: TRANSACTION_STAGING_TABLE.TableName,
214
- Key: { id: mockRecord.id },
215
- ConsistentRead: true
216
- })
217
- )
220
+ expect(retrieveStagedTransaction).toHaveBeenCalledWith(mockRecord.id)
218
221
  expect(docClient.delete).toHaveBeenCalledWith(
219
222
  expect.objectContaining({
220
223
  TableName: TRANSACTION_STAGING_TABLE.TableName,
@@ -248,7 +251,7 @@ describe('transaction service', () => {
248
251
  it('includes a FulfilmentRequest when the permit and contact are for postal fulfilment', async () => {
249
252
  const mockRecord = mockFinalisedTransactionRecord()
250
253
  mockRecord.permissions[0].permitId = MOCK_12MONTH_SENIOR_PERMIT.id
251
- docClient.get.mockResolvedValueOnce({ Item: mockRecord })
254
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
252
255
  await processQueue({ id: mockRecord.id })
253
256
  expect(persist).toBeCalledWith(
254
257
  [
@@ -267,7 +270,7 @@ describe('transaction service', () => {
267
270
  it('does not include a FulfilmentRequest when the permit and contact are not for postal fulfilment', async () => {
268
271
  const mockRecord = mockFinalisedTransactionRecord()
269
272
  mockRecord.permissions[0].permitId = MOCK_1DAY_SENIOR_PERMIT_ENTITY.id
270
- docClient.get.mockResolvedValueOnce({ Item: mockRecord })
273
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
271
274
  await processQueue({ id: mockRecord.id })
272
275
  expect(persist).toBeCalledWith(
273
276
  [
@@ -295,7 +298,7 @@ describe('transaction service', () => {
295
298
  it('does not include a FulfilmentRequest when the permit and contact are for postal fulfilment', async () => {
296
299
  const mockRecord = mockFinalisedTransactionRecord()
297
300
  mockRecord.permissions[0].permitId = MOCK_12MONTH_SENIOR_PERMIT.id
298
- docClient.get.mockResolvedValueOnce({ Item: mockRecord })
301
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
299
302
  await processQueue({ id: mockRecord.id })
300
303
  expect(persist).toBeCalledWith(
301
304
  [
@@ -313,7 +316,7 @@ describe('transaction service', () => {
313
316
  it('does not include a FulfilmentRequest when the permit and contact are not for postal fulfilment', async () => {
314
317
  const mockRecord = mockFinalisedTransactionRecord()
315
318
  mockRecord.permissions[0].permitId = MOCK_1DAY_SENIOR_PERMIT_ENTITY.id
316
- docClient.get.mockResolvedValueOnce({ Item: mockRecord })
319
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
317
320
  await processQueue({ id: mockRecord.id })
318
321
  expect(persist).toBeCalledWith(
319
322
  [
@@ -332,7 +335,7 @@ describe('transaction service', () => {
332
335
  it('sets isLicenceForYou to Yes on the transaction, if it is true on the permission', async () => {
333
336
  const mockRecord = mockFinalisedTransactionRecord()
334
337
  mockRecord.permissions[0].isLicenceForYou = true
335
- docClient.get.mockResolvedValueOnce({ Item: mockRecord })
338
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
336
339
  await processQueue({ id: mockRecord.id })
337
340
  const persistMockFirstAgument = persist.mock.calls[0]
338
341
  expect(persistMockFirstAgument[0][4].isLicenceForYou).toBeDefined()
@@ -342,7 +345,7 @@ describe('transaction service', () => {
342
345
  it('sets isLicenceForYou to No on the transaction, if it is false on the permission', async () => {
343
346
  const mockRecord = mockFinalisedTransactionRecord()
344
347
  mockRecord.permissions[0].isLicenceForYou = false
345
- docClient.get.mockResolvedValueOnce({ Item: mockRecord })
348
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
346
349
  await processQueue({ id: mockRecord.id })
347
350
  const persistMockFirstAgument = persist.mock.calls[0]
348
351
  expect(persistMockFirstAgument[0][4].isLicenceForYou).toBeDefined()
@@ -352,7 +355,7 @@ describe('transaction service', () => {
352
355
  it('does not set isLicenceForYou on the transaction, if it is undefined on the permission', async () => {
353
356
  const mockRecord = mockFinalisedTransactionRecord()
354
357
  mockRecord.permissions[0].isLicenceForYou = undefined
355
- docClient.get.mockResolvedValueOnce({ Item: mockRecord })
358
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
356
359
  await processQueue({ id: mockRecord.id })
357
360
  const persistMockFirstAgument = persist.mock.calls[0]
358
361
  expect(persistMockFirstAgument[0][4].isLicenceForYou).toBeUndefined()
@@ -361,7 +364,7 @@ describe('transaction service', () => {
361
364
  it('does not set isLicenceForYou on the transaction, if it is null on the permission', async () => {
362
365
  const mockRecord = mockFinalisedTransactionRecord()
363
366
  mockRecord.permissions[0].isLicenceForYou = null
364
- docClient.get.mockResolvedValueOnce({ Item: mockRecord })
367
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
365
368
  await processQueue({ id: mockRecord.id })
366
369
  const persistMockFirstAgument = persist.mock.calls[0]
367
370
  expect(persistMockFirstAgument[0][4].isLicenceForYou).toBeUndefined()
@@ -371,7 +374,7 @@ describe('transaction service', () => {
371
374
  const transactionFilename = 'test-file.xml'
372
375
  const mockRecord = mockFinalisedTransactionRecord()
373
376
  mockRecord.transactionFile = transactionFilename
374
- docClient.get.mockResolvedValueOnce({ Item: mockRecord })
377
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
375
378
  const transactionToFileBindingSpy = jest.spyOn(Transaction.prototype, 'bindToAlternateKey')
376
379
  const permissionToFileBindingSpy = jest.spyOn(Permission.prototype, 'bindToAlternateKey')
377
380
  await processQueue({ id: mockRecord.id })
@@ -379,22 +382,144 @@ describe('transaction service', () => {
379
382
  expect(permissionToFileBindingSpy).toHaveBeenCalledWith(Permission.definition.relationships.poclFile, transactionFilename)
380
383
  })
381
384
 
382
- it('throws 404 not found error if a record cannot be found for the given id', async () => {
383
- const mockRecord = mockFinalisedTransactionRecord()
384
- docClient.get.mockResolvedValueOnce({ Item: undefined }).mockResolvedValueOnce({ Item: undefined })
385
- try {
385
+ it('throws an error if retrieveStagedTransaction fails', async () => {
386
+ const error = new Error('Boo!')
387
+ retrieveStagedTransaction.mockRejectedValueOnce(error)
388
+
389
+ await expect(processQueue({ id: 'foo' })).rejects.toThrow(error)
390
+ })
391
+
392
+ describe('when persisting throws an error', () => {
393
+ it('throws the error', async () => {
394
+ const transactionFilename = 'test-file.xml'
395
+ const mockRecord = mockFinalisedTransactionRecord()
396
+ mockRecord.transactionFile = transactionFilename
397
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
398
+
399
+ const error = new Error('I do not like it')
400
+ persist.mockRejectedValueOnce(error)
401
+
402
+ await expect(processQueue({ id: mockRecord.id })).rejects.toThrow(error)
403
+ })
404
+
405
+ it('does not delete the data from the transaction staging table', async () => {
406
+ const transactionFilename = 'test-file.xml'
407
+ const mockRecord = mockFinalisedTransactionRecord()
408
+ mockRecord.transactionFile = transactionFilename
409
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
410
+
411
+ const error = new Error('I do not like it')
412
+ persist.mockRejectedValueOnce(error)
413
+
414
+ try {
415
+ await processQueue({ id: mockRecord.id })
416
+ } catch (e) {}
417
+
418
+ expect(docClient.delete).not.toHaveBeenCalled()
419
+ })
420
+
421
+ it('does not move the data to the transaction staging history table', async () => {
422
+ const transactionFilename = 'test-file.xml'
423
+ const mockRecord = mockFinalisedTransactionRecord()
424
+ mockRecord.transactionFile = transactionFilename
425
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
426
+
427
+ const error = new Error('I do not like it')
428
+ persist.mockRejectedValueOnce(error)
429
+
430
+ try {
431
+ await processQueue({ id: mockRecord.id })
432
+ } catch (e) {}
433
+
434
+ expect(docClient.put).not.toHaveBeenCalled()
435
+ })
436
+ })
437
+
438
+ describe('when persisting throws an PermissionReferenceNumber_Key error', () => {
439
+ it('does not throw an error', async () => {
440
+ const transactionFilename = 'test-file.xml'
441
+ const mockRecord = mockFinalisedTransactionRecord()
442
+ mockRecord.transactionFile = transactionFilename
443
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
444
+
445
+ const error = new Error(
446
+ 'Entity Key PermissionReferenceNumber_Key violated. A record with the same value for Permission Reference Number already exists. A duplicate record cannot be created. Select one or more unique values and try again.'
447
+ )
448
+ persist.mockRejectedValueOnce(error)
449
+
450
+ await expect(processQueue({ id: mockRecord.id })).resolves.not.toThrow()
451
+ })
452
+
453
+ it('logs that the permission has already been persisted', async () => {
454
+ const transactionFilename = 'test-file.xml'
455
+ const mockRecord = mockFinalisedTransactionRecord()
456
+ mockRecord.transactionFile = transactionFilename
457
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
458
+
459
+ const error = new Error(
460
+ 'Entity Key PermissionReferenceNumber_Key violated. A record with the same value for Permission Reference Number already exists. A duplicate record cannot be created. Select one or more unique values and try again.'
461
+ )
462
+ persist.mockRejectedValueOnce(error)
463
+
386
464
  await processQueue({ id: mockRecord.id })
387
- } catch (e) {
388
- expect(e.message).toEqual('A transaction for the specified identifier was not found')
389
- expect(e.output.statusCode).toEqual(404)
390
- }
465
+
466
+ expect(debug).toHaveBeenCalledWith('Permission for staging id %s has already been persisted', mockRecord.id)
467
+ })
468
+
469
+ it('deletes the data from the transaction staging table', async () => {
470
+ const transactionFilename = 'test-file.xml'
471
+ const mockRecord = mockFinalisedTransactionRecord()
472
+ mockRecord.transactionFile = transactionFilename
473
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
474
+
475
+ const error = new Error(
476
+ 'Entity Key PermissionReferenceNumber_Key violated. A record with the same value for Permission Reference Number already exists. A duplicate record cannot be created. Select one or more unique values and try again.'
477
+ )
478
+ persist.mockRejectedValueOnce(error)
479
+
480
+ await processQueue({ id: mockRecord.id })
481
+
482
+ expect(docClient.delete).toHaveBeenCalledWith(
483
+ expect.objectContaining({
484
+ TableName: TRANSACTION_STAGING_TABLE.TableName,
485
+ Key: { id: mockRecord.id }
486
+ })
487
+ )
488
+ })
489
+
490
+ it('moves the data to the transaction staging history table', async () => {
491
+ const transactionFilename = 'test-file.xml'
492
+ const mockRecord = mockFinalisedTransactionRecord()
493
+ mockRecord.transactionFile = transactionFilename
494
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
495
+
496
+ const error = new Error(
497
+ 'Entity Key PermissionReferenceNumber_Key violated. A record with the same value for Permission Reference Number already exists. A duplicate record cannot be created. Select one or more unique values and try again.'
498
+ )
499
+ persist.mockRejectedValueOnce(error)
500
+
501
+ await processQueue({ id: mockRecord.id })
502
+
503
+ const expectedRecord = Object.assign(mockRecord, {
504
+ id: expect.stringMatching(/[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/i),
505
+ expires: expect.any(Number)
506
+ })
507
+
508
+ expect(docClient.put).toHaveBeenCalledWith(
509
+ expect.objectContaining({
510
+ TableName: TRANSACTION_STAGING_HISTORY_TABLE.TableName,
511
+ Item: expectedRecord,
512
+ ConditionExpression: 'attribute_not_exists(id)'
513
+ })
514
+ )
515
+ })
391
516
  })
392
517
 
393
518
  describe.each([20, 38.46, 287])('the provisional transaction amount of £%d is used for final transaction amount', cost => {
394
519
  const setup = async () => {
395
520
  const mockRecord = mockFinalisedTransactionRecord()
396
521
  mockRecord.payment.amount = cost
397
- docClient.get.mockResolvedValueOnce({ Item: mockRecord })
522
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
398
523
  await processQueue({ id: mockRecord.id })
399
524
  const {
400
525
  mock: {
@@ -427,7 +552,7 @@ describe('transaction service', () => {
427
552
  callingArgs.transaction = JSON.parse(JSON.stringify(transaction))
428
553
  })
429
554
  const mockRecord = mockFinalisedTransactionRecord()
430
- docClient.get.mockResolvedValueOnce({ Item: { ...mockRecord } })
555
+ retrieveStagedTransaction.mockResolvedValueOnce({ ...mockRecord })
431
556
  await processQueue({ id: mockRecord.id })
432
557
  // jest.fn args aren't immutable and transaction is changed in processQueue, so we use our clone that hasn't changed
433
558
  expect(callingArgs.transaction).toEqual(mockRecord)
@@ -440,7 +565,7 @@ describe('transaction service', () => {
440
565
  for (const key of keysToCopy) {
441
566
  expectedPermissionData[key] = mockRecord.permissions[0][key]
442
567
  }
443
- docClient.get.mockResolvedValueOnce({ Item: mockRecord })
568
+ retrieveStagedTransaction.mockResolvedValueOnce(mockRecord)
444
569
 
445
570
  await processQueue({ id: mockRecord.id })
446
571
 
@@ -451,7 +576,7 @@ describe('transaction service', () => {
451
576
  const rprSymbol = Symbol('rpr')
452
577
  const finalisedTransaction = mockFinalisedTransactionRecord()
453
578
  generateRecurringPaymentRecord.mockReturnValueOnce(rprSymbol)
454
- docClient.get.mockResolvedValueOnce({ Item: finalisedTransaction })
579
+ retrieveStagedTransaction.mockResolvedValueOnce(finalisedTransaction)
455
580
  await processQueue({ id: finalisedTransaction.id })
456
581
  expect(processRecurringPayment).toHaveBeenCalledWith(rprSymbol, expect.any(Contact))
457
582
  })
@@ -464,7 +589,7 @@ describe('transaction service', () => {
464
589
  const finalisedTransaction = mockFinalisedTransactionRecord()
465
590
  findNewestExistingRecurringPaymentInCrm.mockReturnValueOnce(mockExistingRecurringPayment)
466
591
  processRecurringPayment.mockReturnValueOnce({ recurringPayment: mockNewRecurringPayment })
467
- docClient.get.mockResolvedValueOnce({ Item: finalisedTransaction })
592
+ retrieveStagedTransaction.mockResolvedValueOnce(finalisedTransaction)
468
593
  await processQueue({ id: finalisedTransaction.id })
469
594
  expect(mockExistingRecurringPayment.bindToEntity).toHaveBeenCalledWith(
470
595
  RecurringPayment.definition.relationships.nextRecurringPayment,
@@ -0,0 +1,100 @@
1
+ import Boom from '@hapi/boom'
2
+ import db from 'debug'
3
+ import { retrieveStagedTransaction } from '../retrieve-transaction.js'
4
+ import { TRANSACTION_STAGING_TABLE, TRANSACTION_STAGING_HISTORY_TABLE } from '../../../config.js'
5
+ import { AWS } from '@defra-fish/connectors-lib'
6
+ const { docClient } = AWS.mock.results[0].value
7
+ const { value: debug } = db.mock.results[db.mock.calls.findIndex(c => c[0] === 'sales:transactions')]
8
+
9
+ jest.mock('debug', () => jest.fn(() => jest.fn()))
10
+
11
+ jest.mock('@defra-fish/connectors-lib', () => {
12
+ const mockAWS = {
13
+ docClient: {
14
+ get: jest.fn()
15
+ }
16
+ }
17
+ return {
18
+ AWS: jest.fn(() => mockAWS)
19
+ }
20
+ })
21
+
22
+ const mockResultWithItem = (item = 'foo') => ({
23
+ Item: item
24
+ })
25
+
26
+ describe('retrieve-transaction', () => {
27
+ it('checks the staging table for a match', async () => {
28
+ const id = 'foo'
29
+ docClient.get.mockResolvedValueOnce(mockResultWithItem())
30
+
31
+ await retrieveStagedTransaction(id)
32
+
33
+ expect(docClient.get).toHaveBeenCalledWith({ TableName: TRANSACTION_STAGING_TABLE.TableName, Key: { id }, ConsistentRead: true })
34
+ })
35
+
36
+ describe('when the result has an item', () => {
37
+ it('logs that it has retrieved a record', async () => {
38
+ const id = 'foo'
39
+ docClient.get.mockResolvedValueOnce(mockResultWithItem())
40
+
41
+ await retrieveStagedTransaction(id)
42
+
43
+ expect(debug).toHaveBeenCalledWith('Retrieved transaction record for staging id %s', id)
44
+ })
45
+
46
+ it('returns the result item', async () => {
47
+ const expectedItem = Symbol('Item')
48
+ docClient.get.mockResolvedValueOnce(mockResultWithItem(expectedItem))
49
+
50
+ const transactionResult = await retrieveStagedTransaction('foo')
51
+
52
+ expect(transactionResult).toEqual(expectedItem)
53
+ })
54
+ })
55
+
56
+ describe('when the result does not have an item', () => {
57
+ it('logs that it has failed to retrieve a record', async () => {
58
+ const id = 'foo'
59
+ docClient.get.mockResolvedValueOnce({}).mockResolvedValueOnce(mockResultWithItem())
60
+
61
+ try {
62
+ await retrieveStagedTransaction(id)
63
+ } catch {
64
+ expect(debug).toHaveBeenCalledWith('Failed to retrieve a transaction with staging id %s', id)
65
+ }
66
+ })
67
+
68
+ it('checks the historical staging table for a match', async () => {
69
+ const id = 'foo'
70
+ docClient.get.mockResolvedValueOnce({}).mockResolvedValueOnce(mockResultWithItem())
71
+
72
+ try {
73
+ await retrieveStagedTransaction(id)
74
+ } catch {
75
+ expect(docClient.get).toHaveBeenCalledWith({
76
+ TableName: TRANSACTION_STAGING_HISTORY_TABLE.TableName,
77
+ Key: { id },
78
+ ConsistentRead: true
79
+ })
80
+ }
81
+ })
82
+
83
+ it('throws a 410 Gone error if there is not a historical match', async () => {
84
+ const historicalItem = Symbol('historicalItem')
85
+ docClient.get.mockResolvedValueOnce({}).mockResolvedValueOnce(mockResultWithItem(historicalItem))
86
+
87
+ await expect(retrieveStagedTransaction('foo')).rejects.toThrow(
88
+ Boom.resourceGone('The transaction has already been finalised', historicalItem)
89
+ )
90
+ })
91
+
92
+ it('throws a 404 Not Found error if there is not a historical match', async () => {
93
+ docClient.get.mockResolvedValueOnce({}).mockResolvedValueOnce({})
94
+
95
+ await expect(retrieveStagedTransaction('foo')).rejects.toThrow(
96
+ Boom.notFound('A transaction for the specified identifier was not found')
97
+ )
98
+ })
99
+ })
100
+ })
@@ -102,7 +102,9 @@ export async function processQueue ({ id }) {
102
102
  paymentJournal.total = totalTransactionValue
103
103
 
104
104
  debug('Persisting %d entities for staging id %s', entities.length, id)
105
- await persist(entities, transactionRecord.createdBy)
105
+
106
+ await persistEntities(entities, transactionRecord, id)
107
+
106
108
  debug('Moving staging data to history table for staging id %s', id)
107
109
  await docClient.delete({ TableName: TRANSACTION_STAGING_TABLE.TableName, Key: { id } })
108
110
  await docClient.put({
@@ -172,6 +174,20 @@ const createTransactionEntities = async transactionRecord => {
172
174
  return { transaction, chargeJournal, paymentJournal }
173
175
  }
174
176
 
177
+ const persistEntities = async (entities, transactionRecord, id) => {
178
+ const ALREADY_PERSISTED_ERROR = /Permission Reference Number already exists/
179
+
180
+ try {
181
+ await persist(entities, transactionRecord.createdBy)
182
+ } catch (e) {
183
+ if (e.message.match(ALREADY_PERSISTED_ERROR)) {
184
+ debug('Permission for staging id %s has already been persisted', id)
185
+ } else {
186
+ throw e
187
+ }
188
+ }
189
+ }
190
+
175
191
  export const getTransactionJournalRefNumber = (transactionRecord, type) => {
176
192
  if (POCL_TRANSACTION_SOURCES.includes(transactionRecord.dataSource) && type === 'Payment') {
177
193
  if (transactionRecord.dataSource === DDE_DATA_SOURCE && transactionRecord.journalId) {