@mojaloop/central-ledger 17.7.8 → 17.8.0-orb.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.
Files changed (147) hide show
  1. package/.ncurc.yaml +3 -1
  2. package/.nvmrc +1 -1
  3. package/.nycrc.yml +16 -1
  4. package/Dockerfile +9 -10
  5. package/README.md +39 -9
  6. package/audit-ci.jsonc +15 -2
  7. package/config/default.json +17 -1
  8. package/docker/central-ledger/default.json +9 -0
  9. package/docker/config-modifier/configs/central-ledger.js +25 -0
  10. package/docker/env.sh +15 -0
  11. package/docker/kafka/scripts/provision.sh +4 -1
  12. package/docker/ml-api-adapter/default.json +4 -1
  13. package/docker-compose.yml +90 -2
  14. package/documentation/db/erd-transfer-timeout.png +0 -0
  15. package/documentation/db/erd-transfer-timeout.txt +81 -0
  16. package/documentation/fx-implementation/README.md +48 -0
  17. package/documentation/fx-implementation/assets/fx-position-movements.drawio.svg +4 -0
  18. package/documentation/fx-implementation/assets/test-scenario.drawio.svg +4 -0
  19. package/documentation/sequence-diagrams/Handler - FX timeout.plantuml +123 -0
  20. package/documentation/sequence-diagrams/Handler - FX timeout.png +0 -0
  21. package/documentation/sequence-diagrams/Handler - timeout.plantuml +81 -0
  22. package/documentation/sequence-diagrams/Handler - timeout.png +0 -0
  23. package/documentation/state-diagrams/transfer-ML-spec-states-diagram.png +0 -0
  24. package/documentation/state-diagrams/transfer-internal-states-diagram.png +0 -0
  25. package/documentation/state-diagrams/transfer-internal-states.plantuml +75 -0
  26. package/documentation/state-diagrams/transfer-states.plantuml +13 -0
  27. package/migrations/310204_transferParticipant-participantId.js +52 -0
  28. package/migrations/310403_participantPositionChange-participantCurrencyId.js +47 -0
  29. package/migrations/310404_participantPositionChange-change.js +46 -0
  30. package/migrations/600010_fxTransferType.js +43 -0
  31. package/migrations/600011_fxTransferType-indexes.js +38 -0
  32. package/migrations/600012_fxParticipantCurrencyType.js +43 -0
  33. package/migrations/600013_fxParticipantCurrencyType-indexes.js +38 -0
  34. package/migrations/600100_fxTransferDuplicateCheck.js +42 -0
  35. package/migrations/600110_fxTransferErrorDuplicateCheck.js.js +17 -0
  36. package/migrations/600200_fxTransfer.js +51 -0
  37. package/migrations/600201_fxTransfer-indexes.js +40 -0
  38. package/migrations/600400_fxTransferStateChange.js +46 -0
  39. package/migrations/600401_fxTransferStateChange-indexes.js +40 -0
  40. package/migrations/600501_fxWatchList.js +46 -0
  41. package/migrations/600502_fxWatchList-indexes.js +40 -0
  42. package/migrations/600600_fxTransferFulfilmentDuplicateCheck.js +43 -0
  43. package/migrations/600601_fxTransferFulfilmentDuplicateCheck-indexes.js +38 -0
  44. package/migrations/600700_fxTransferFulfilment.js +47 -0
  45. package/migrations/600701_fxTransferFulfilment-indexes.js +43 -0
  46. package/migrations/600800_fxTransferExtension.js +47 -0
  47. package/migrations/601400_fxTransferTimeout.js +43 -0
  48. package/migrations/601401_fxTransferTimeout-indexes.js +37 -0
  49. package/migrations/601500_fxTransferError.js +44 -0
  50. package/migrations/601501_fxTransferError-indexes.js +37 -0
  51. package/migrations/610200_fxTransferParticipant.js +52 -0
  52. package/migrations/610201_fxTransferParticipant-indexes.js +44 -0
  53. package/migrations/610202_fxTransferParticipant-participantId.js +52 -0
  54. package/migrations/610403_participantPositionChange-fxTransfer.js +46 -0
  55. package/migrations/910101_feature904DataMigration.js +46 -52
  56. package/migrations/910102_feature949DataMigration.js +219 -225
  57. package/migrations/950104_settlementModel-settlementAccountTypeId.js +15 -20
  58. package/migrations/950108_participantProxy.js +18 -0
  59. package/migrations/950109_fxQuote.js +19 -0
  60. package/migrations/950110_fxQuoteResponse.js +25 -0
  61. package/migrations/950111_fxQuoteError.js +23 -0
  62. package/migrations/950113_fxQuoteDuplicateCheck.js +18 -0
  63. package/migrations/950114_fxQuoteResponseDuplicateCheck.js +21 -0
  64. package/migrations/950115_fxQuoteConversionTerms.js +36 -0
  65. package/migrations/950116_fxQuoteConversionTermsExtension.js +21 -0
  66. package/migrations/950117_fxQuoteResponseConversionTerms.js +39 -0
  67. package/migrations/950118_fxQuoteResponseConversionTermsExtension.js +21 -0
  68. package/migrations/950119_fxCharge.js +27 -0
  69. package/migrations/960100_create_externalParticipant.js +47 -0
  70. package/migrations/960110_alter_transferParticipant__addFiled_externalParticipantId.js +50 -0
  71. package/migrations/960111_alter_fxTransferParticipant__addFiled_externalParticipantId.js +50 -0
  72. package/package.json +27 -20
  73. package/seeds/endpointType.js +18 -0
  74. package/seeds/fxParticipantCurrencyType.js +45 -0
  75. package/seeds/fxTransferType.js +45 -0
  76. package/seeds/participant.js +2 -1
  77. package/seeds/transferParticipantRoleType.js +9 -0
  78. package/seeds/transferState.js +10 -0
  79. package/src/api/interface/swagger.json +23 -30
  80. package/src/api/participants/handler.js +6 -2
  81. package/src/api/participants/routes.js +8 -7
  82. package/src/api/root/handler.js +15 -5
  83. package/src/domain/fx/cyril.js +466 -0
  84. package/src/domain/fx/index.js +107 -0
  85. package/src/domain/participant/index.js +108 -1
  86. package/src/domain/position/abort.js +215 -0
  87. package/src/domain/position/binProcessor.js +361 -99
  88. package/src/domain/position/fulfil.js +252 -111
  89. package/src/domain/position/fx-fulfil.js +138 -0
  90. package/src/domain/position/fx-prepare.js +280 -0
  91. package/src/domain/position/fx-timeout-reserved.js +159 -0
  92. package/src/domain/position/index.js +1 -0
  93. package/src/domain/position/prepare.js +69 -49
  94. package/src/domain/position/timeout-reserved.js +162 -0
  95. package/src/domain/timeout/index.js +26 -2
  96. package/src/domain/transfer/index.js +22 -5
  97. package/src/domain/transfer/transform.js +19 -6
  98. package/src/handlers/admin/handler.js +0 -2
  99. package/src/handlers/bulk/fulfil/handler.js +5 -5
  100. package/src/handlers/bulk/get/handler.js +5 -5
  101. package/src/handlers/bulk/prepare/handler.js +9 -9
  102. package/src/handlers/bulk/processing/handler.js +8 -7
  103. package/src/handlers/bulk/shared/validator.js +1 -1
  104. package/src/handlers/positions/handler.js +23 -10
  105. package/src/handlers/positions/handlerBatch.js +54 -26
  106. package/src/handlers/register.js +2 -1
  107. package/src/handlers/timeouts/handler.js +212 -68
  108. package/src/handlers/transfers/FxFulfilService.js +387 -0
  109. package/src/handlers/transfers/createRemittanceEntity.js +106 -0
  110. package/src/handlers/transfers/dto.js +53 -0
  111. package/src/handlers/transfers/handler.js +609 -571
  112. package/src/handlers/transfers/prepare.js +572 -0
  113. package/src/handlers/transfers/validator.js +75 -14
  114. package/src/lib/cache.js +1 -1
  115. package/src/lib/config.js +6 -3
  116. package/src/lib/healthCheck/subServiceHealth.js +12 -2
  117. package/src/lib/proxyCache.js +131 -0
  118. package/src/models/bulkTransfer/facade.js +58 -82
  119. package/src/models/fxTransfer/duplicateCheck.js +153 -0
  120. package/src/models/fxTransfer/fxTransfer.js +578 -0
  121. package/src/models/fxTransfer/fxTransferError.js +53 -0
  122. package/src/models/fxTransfer/fxTransferExtension.js +41 -0
  123. package/src/models/fxTransfer/fxTransferTimeout.js +68 -0
  124. package/src/models/fxTransfer/index.js +15 -0
  125. package/src/models/fxTransfer/stateChange.js +47 -0
  126. package/src/models/fxTransfer/watchList.js +49 -0
  127. package/src/models/ledgerAccountType/ledgerAccountType.js +8 -32
  128. package/src/models/misc/segment.js +0 -1
  129. package/src/models/participant/externalParticipant.js +96 -0
  130. package/src/models/participant/externalParticipantCached.js +148 -0
  131. package/src/models/participant/facade.js +192 -123
  132. package/src/models/participant/participant.js +2 -1
  133. package/src/models/participant/participantCurrency.js +1 -1
  134. package/src/models/participant/participantPosition.js +2 -8
  135. package/src/models/position/batch.js +90 -1
  136. package/src/models/position/facade.js +5 -5
  137. package/src/models/position/participantPositionChanges.js +68 -0
  138. package/src/models/settlement/settlementModel.js +5 -23
  139. package/src/models/transfer/facade.js +806 -447
  140. package/src/shared/constants.js +52 -0
  141. package/src/shared/fspiopErrorFactory.js +131 -0
  142. package/src/shared/logger/index.js +8 -0
  143. package/src/shared/loggingPlugin.js +43 -0
  144. package/src/shared/plugins.js +6 -0
  145. package/src/shared/setup.js +10 -0
  146. package/test-integration.Dockerfile +1 -1
  147. package/test.Dockerfile +1 -1
@@ -23,6 +23,7 @@
23
23
  * Rajiv Mothilal <rajiv.mothilal@modusbox.com>
24
24
  * Miguel de Barros <miguel.debarros@modusbox.com>
25
25
  * Shashikant Hirugade <shashikant.hirugade@modusbox.com>
26
+ * Vijay Kumar Guthi <vijaya.guthi@infitx.com>
26
27
  --------------
27
28
  ******/
28
29
 
@@ -32,19 +33,21 @@
32
33
  * @module src/models/transfer/facade/
33
34
  */
34
35
 
35
- const Db = require('../../lib/db')
36
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
37
+ const Metrics = require('@mojaloop/central-services-metrics')
38
+ const MLNumber = require('@mojaloop/ml-number')
36
39
  const Enum = require('@mojaloop/central-services-shared').Enum
37
- const TransferEventAction = Enum.Events.Event.Action
38
- const TransferInternalState = Enum.Transfers.TransferInternalState
39
- const TransferExtensionModel = require('./transferExtension')
40
- const ParticipantFacade = require('../participant/facade')
41
40
  const Time = require('@mojaloop/central-services-shared').Util.Time
42
- const MLNumber = require('@mojaloop/ml-number')
41
+
42
+ const { logger } = require('../../shared/logger')
43
+ const Db = require('../../lib/db')
43
44
  const Config = require('../../lib/config')
44
- const _ = require('lodash')
45
- const ErrorHandler = require('@mojaloop/central-services-error-handling')
46
- const Logger = require('@mojaloop/central-services-logger')
47
- const Metrics = require('@mojaloop/central-services-metrics')
45
+ const ParticipantFacade = require('../participant/facade')
46
+ const ParticipantCachedModel = require('../participant/participantCached')
47
+ const TransferExtensionModel = require('./transferExtension')
48
+
49
+ const TransferEventAction = Enum.Events.Event.Action
50
+ const TransferInternalState = Enum.Transfers.TransferInternalState
48
51
 
49
52
  // Alphabetically ordered list of error texts used below
50
53
  const UnsupportedActionText = 'Unsupported action'
@@ -53,24 +56,25 @@ const getById = async (id) => {
53
56
  try {
54
57
  /** @namespace Db.transfer **/
55
58
  return await Db.from('transfer').query(async (builder) => {
59
+ /* istanbul ignore next */
56
60
  const transferResult = await builder
57
61
  .where({
58
62
  'transfer.transferId': id,
59
63
  'tprt1.name': 'PAYER_DFSP', // TODO: refactor to use transferParticipantRoleTypeId
60
64
  'tprt2.name': 'PAYEE_DFSP'
61
65
  })
62
- .whereRaw('pc1.currencyId = transfer.currencyId')
63
- .whereRaw('pc2.currencyId = transfer.currencyId')
64
66
  // PAYER
65
67
  .innerJoin('transferParticipant AS tp1', 'tp1.transferId', 'transfer.transferId')
68
+ .leftJoin('externalParticipant AS ep1', 'ep1.externalParticipantId', 'tp1.externalParticipantId')
66
69
  .innerJoin('transferParticipantRoleType AS tprt1', 'tprt1.transferParticipantRoleTypeId', 'tp1.transferParticipantRoleTypeId')
67
- .innerJoin('participantCurrency AS pc1', 'pc1.participantCurrencyId', 'tp1.participantCurrencyId')
68
- .innerJoin('participant AS da', 'da.participantId', 'pc1.participantId')
70
+ .innerJoin('participant AS da', 'da.participantId', 'tp1.participantId')
71
+ .leftJoin('participantCurrency AS pc1', 'pc1.participantCurrencyId', 'tp1.participantCurrencyId')
69
72
  // PAYEE
70
73
  .innerJoin('transferParticipant AS tp2', 'tp2.transferId', 'transfer.transferId')
74
+ .leftJoin('externalParticipant AS ep2', 'ep2.externalParticipantId', 'tp2.externalParticipantId')
71
75
  .innerJoin('transferParticipantRoleType AS tprt2', 'tprt2.transferParticipantRoleTypeId', 'tp2.transferParticipantRoleTypeId')
72
- .innerJoin('participantCurrency AS pc2', 'pc2.participantCurrencyId', 'tp2.participantCurrencyId')
73
- .innerJoin('participant AS ca', 'ca.participantId', 'pc2.participantId')
76
+ .innerJoin('participant AS ca', 'ca.participantId', 'tp2.participantId')
77
+ .leftJoin('participantCurrency AS pc2', 'pc2.participantCurrencyId', 'tp2.participantCurrencyId')
74
78
  // OTHER JOINS
75
79
  .innerJoin('ilpPacket AS ilpp', 'ilpp.transferId', 'transfer.transferId')
76
80
  .leftJoin('transferStateChange AS tsc', 'tsc.transferId', 'transfer.transferId')
@@ -84,10 +88,12 @@ const getById = async (id) => {
84
88
  'tp1.amount AS payerAmount',
85
89
  'da.participantId AS payerParticipantId',
86
90
  'da.name AS payerFsp',
91
+ 'da.isProxy AS payerIsProxy',
87
92
  'pc2.participantCurrencyId AS payeeParticipantCurrencyId',
88
93
  'tp2.amount AS payeeAmount',
89
94
  'ca.participantId AS payeeParticipantId',
90
95
  'ca.name AS payeeFsp',
96
+ 'ca.isProxy AS payeeIsProxy',
91
97
  'tsc.transferStateChangeId',
92
98
  'tsc.transferStateId AS transferState',
93
99
  'tsc.reason AS reason',
@@ -98,10 +104,13 @@ const getById = async (id) => {
98
104
  'transfer.ilpCondition AS condition',
99
105
  'tf.ilpFulfilment AS fulfilment',
100
106
  'te.errorCode',
101
- 'te.errorDescription'
107
+ 'te.errorDescription',
108
+ 'ep1.name AS externalPayerName',
109
+ 'ep2.name AS externalPayeeName'
102
110
  )
103
111
  .orderBy('tsc.transferStateChangeId', 'desc')
104
112
  .first()
113
+
105
114
  if (transferResult) {
106
115
  transferResult.extensionList = await TransferExtensionModel.getByTransferId(id) // TODO: check if this is needed
107
116
  if (transferResult.errorCode && transferResult.transferStateEnumeration === Enum.Transfers.TransferState.ABORTED) {
@@ -116,6 +125,7 @@ const getById = async (id) => {
116
125
  return transferResult
117
126
  })
118
127
  } catch (err) {
128
+ logger.warn('error in transfer.getById', err)
119
129
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
120
130
  }
121
131
  }
@@ -168,6 +178,7 @@ const getByIdLight = async (id) => {
168
178
  return transferResult
169
179
  })
170
180
  } catch (err) {
181
+ logger.warn('error in transfer.getByIdLight', err)
171
182
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
172
183
  }
173
184
  }
@@ -222,6 +233,7 @@ const getAll = async () => {
222
233
  return transferResultList
223
234
  })
224
235
  } catch (err) {
236
+ logger.warn('error in transfer.getAll', err)
225
237
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
226
238
  }
227
239
  }
@@ -237,8 +249,10 @@ const getTransferInfoToChangePosition = async (id, transferParticipantRoleTypeId
237
249
  'transferParticipant.ledgerEntryTypeId': ledgerEntryTypeId
238
250
  })
239
251
  .innerJoin('transferStateChange AS tsc', 'tsc.transferId', 'transferParticipant.transferId')
252
+ .innerJoin('transfer AS t', 't.transferId', 'transferParticipant.transferId')
240
253
  .select(
241
254
  'transferParticipant.*',
255
+ 't.currencyId',
242
256
  'tsc.transferStateId',
243
257
  'tsc.reason'
244
258
  )
@@ -246,6 +260,7 @@ const getTransferInfoToChangePosition = async (id, transferParticipantRoleTypeId
246
260
  .first()
247
261
  })
248
262
  } catch (err) {
263
+ logger.warn('error in getTransferInfoToChangePosition', err)
249
264
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
250
265
  }
251
266
  }
@@ -353,12 +368,12 @@ const savePayeeTransferResponse = async (transferId, payload, action, fspiopErro
353
368
  .orderBy('changedDate', 'desc')
354
369
  })
355
370
  transferFulfilmentRecord.settlementWindowId = res[0].settlementWindowId
356
- Logger.isDebugEnabled && Logger.debug('savePayeeTransferResponse::settlementWindowId')
371
+ logger.debug('savePayeeTransferResponse::settlementWindowId')
357
372
  }
358
373
  if (isFulfilment) {
359
374
  await knex('transferFulfilment').transacting(trx).insert(transferFulfilmentRecord)
360
375
  result.transferFulfilmentRecord = transferFulfilmentRecord
361
- Logger.isDebugEnabled && Logger.debug('savePayeeTransferResponse::transferFulfilment')
376
+ logger.debug('savePayeeTransferResponse::transferFulfilment')
362
377
  }
363
378
  if (transferExtensionRecordsList.length > 0) {
364
379
  // ###! CAN BE DONE THROUGH A BATCH
@@ -367,11 +382,11 @@ const savePayeeTransferResponse = async (transferId, payload, action, fspiopErro
367
382
  }
368
383
  // ###!
369
384
  result.transferExtensionRecordsList = transferExtensionRecordsList
370
- Logger.isDebugEnabled && Logger.debug('savePayeeTransferResponse::transferExtensionRecordsList')
385
+ logger.debug('savePayeeTransferResponse::transferExtensionRecordsList')
371
386
  }
372
387
  await knex('transferStateChange').transacting(trx).insert(transferStateChangeRecord)
373
388
  result.transferStateChangeRecord = transferStateChangeRecord
374
- Logger.isDebugEnabled && Logger.debug('savePayeeTransferResponse::transferStateChange')
389
+ logger.debug('savePayeeTransferResponse::transferStateChange')
375
390
  if (fspiopError) {
376
391
  const insertedTransferStateChange = await knex('transferStateChange').transacting(trx)
377
392
  .where({ transferId })
@@ -380,45 +395,81 @@ const savePayeeTransferResponse = async (transferId, payload, action, fspiopErro
380
395
  transferErrorRecord.transferStateChangeId = insertedTransferStateChange.transferStateChangeId
381
396
  await knex('transferError').transacting(trx).insert(transferErrorRecord)
382
397
  result.transferErrorRecord = transferErrorRecord
383
- Logger.isDebugEnabled && Logger.debug('savePayeeTransferResponse::transferError')
398
+ logger.debug('savePayeeTransferResponse::transferError')
384
399
  }
385
400
  histTPayeeResponseValidationPassedEnd({ success: true, queryName: 'facade_saveTransferPrepared_transaction' })
386
401
  result.savePayeeTransferResponseExecuted = true
387
- Logger.isDebugEnabled && Logger.debug('savePayeeTransferResponse::success')
402
+ logger.debug('savePayeeTransferResponse::success')
388
403
  } catch (err) {
389
- await trx.rollback()
404
+ logger.error('savePayeeTransferResponse::failure', err)
390
405
  histTPayeeResponseValidationPassedEnd({ success: false, queryName: 'facade_saveTransferPrepared_transaction' })
391
- Logger.isErrorEnabled && Logger.error('savePayeeTransferResponse::failure')
392
406
  throw err
393
407
  }
394
408
  })
395
409
  histTimerSavePayeeTranferResponsedEnd({ success: true, queryName: 'facade_savePayeeTransferResponse' })
396
410
  return result
397
411
  } catch (err) {
412
+ logger.warn('error in savePayeeTransferResponse', err)
398
413
  histTimerSavePayeeTranferResponsedEnd({ success: false, queryName: 'facade_savePayeeTransferResponse' })
399
414
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
400
415
  }
401
416
  }
402
417
 
403
- const saveTransferPrepared = async (payload, stateReason = null, hasPassedValidation = true) => {
418
+ /**
419
+ * Saves prepare transfer details to DB.
420
+ *
421
+ * @param {Object} payload - Message payload.
422
+ * @param {string | null} stateReason - Validation failure reasons.
423
+ * @param {Boolean} hasPassedValidation - Is transfer prepare validation passed.
424
+ * @param {DeterminingTransferCheckResult} determiningTransferCheckResult - Determining transfer check result.
425
+ * @param {ProxyObligation} proxyObligation - The proxy obligation
426
+ * @returns {Promise<void>}
427
+ */
428
+ const saveTransferPrepared = async (payload, stateReason = null, hasPassedValidation = true, determiningTransferCheckResult, proxyObligation) => {
404
429
  const histTimerSaveTransferPreparedEnd = Metrics.getHistogram(
405
430
  'model_transfer',
406
431
  'facade_saveTransferPrepared - Metrics for transfer model',
407
432
  ['success', 'queryName']
408
433
  ).startTimer()
409
434
  try {
410
- const participants = []
411
- const names = [payload.payeeFsp, payload.payerFsp]
435
+ const participants = {
436
+ [payload.payeeFsp]: {},
437
+ [payload.payerFsp]: {}
438
+ }
412
439
 
413
- for (const name of names) {
414
- const participant = await ParticipantFacade.getByNameAndCurrency(name, payload.amount.currency, Enum.Accounts.LedgerAccountType.POSITION)
440
+ // Iterate over the participants and get the details
441
+ for (const name of Object.keys(participants)) {
442
+ const participant = await ParticipantCachedModel.getByName(name)
415
443
  if (participant) {
416
- participants.push(participant)
444
+ participants[name].id = participant.participantId
445
+ }
446
+ // If determiningTransferCheckResult.participantCurrencyValidationList contains the participant name, then get the participantCurrencyId
447
+ const participantCurrency = determiningTransferCheckResult && determiningTransferCheckResult.participantCurrencyValidationList.find(participantCurrencyItem => participantCurrencyItem.participantName === name)
448
+ if (participantCurrency) {
449
+ const participantCurrencyRecord = await ParticipantFacade.getByNameAndCurrency(participantCurrency.participantName, participantCurrency.currencyId, Enum.Accounts.LedgerAccountType.POSITION)
450
+ participants[name].participantCurrencyId = participantCurrencyRecord?.participantCurrencyId
417
451
  }
418
452
  }
419
453
 
420
- const participantCurrencyIds = await _.reduce(participants, (m, acct) =>
421
- _.set(m, acct.name, acct.participantCurrencyId), {})
454
+ if (proxyObligation?.isInitiatingFspProxy) {
455
+ const proxyId = proxyObligation.initiatingFspProxyOrParticipantId.proxyId
456
+ const proxyParticipant = await ParticipantCachedModel.getByName(proxyId)
457
+ participants[proxyId] = {}
458
+ participants[proxyId].id = proxyParticipant.participantId
459
+ const participantCurrencyRecord = await ParticipantFacade.getByNameAndCurrency(
460
+ proxyId, payload.amount.currency, Enum.Accounts.LedgerAccountType.POSITION
461
+ )
462
+ // In a regional scheme, the stand-in initiating FSP proxy may not have a participantCurrencyId
463
+ // of the target currency of the transfer, so set to null if not found
464
+ participants[proxyId].participantCurrencyId = participantCurrencyRecord?.participantCurrencyId
465
+ }
466
+
467
+ if (proxyObligation?.isCounterPartyFspProxy) {
468
+ const proxyId = proxyObligation.counterPartyFspProxyOrParticipantId.proxyId
469
+ const proxyParticipant = await ParticipantCachedModel.getByName(proxyId)
470
+ participants[proxyId] = {}
471
+ participants[proxyId].id = proxyParticipant.participantId
472
+ }
422
473
 
423
474
  const transferRecord = {
424
475
  transferId: payload.transferId,
@@ -433,29 +484,60 @@ const saveTransferPrepared = async (payload, stateReason = null, hasPassedValida
433
484
  value: payload.ilpPacket
434
485
  }
435
486
 
436
- const state = ((hasPassedValidation) ? Enum.Transfers.TransferInternalState.RECEIVED_PREPARE : Enum.Transfers.TransferInternalState.INVALID)
437
-
438
487
  const transferStateChangeRecord = {
439
488
  transferId: payload.transferId,
440
- transferStateId: state,
489
+ transferStateId: hasPassedValidation ? TransferInternalState.RECEIVED_PREPARE : TransferInternalState.INVALID,
441
490
  reason: stateReason,
442
491
  createdDate: Time.getUTCString(new Date())
443
492
  }
444
493
 
445
- const payerTransferParticipantRecord = {
446
- transferId: payload.transferId,
447
- participantCurrencyId: participantCurrencyIds[payload.payerFsp],
448
- transferParticipantRoleTypeId: Enum.Accounts.TransferParticipantRoleType.PAYER_DFSP,
449
- ledgerEntryTypeId: Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE,
450
- amount: payload.amount.amount
494
+ let payerTransferParticipantRecord
495
+ if (proxyObligation?.isInitiatingFspProxy) {
496
+ const externalParticipantId = await ParticipantFacade.getExternalParticipantIdByNameOrCreate(proxyObligation.initiatingFspProxyOrParticipantId)
497
+ // todo: think, what if externalParticipantId is null?
498
+ payerTransferParticipantRecord = {
499
+ transferId: payload.transferId,
500
+ participantId: participants[proxyObligation.initiatingFspProxyOrParticipantId.proxyId].id,
501
+ participantCurrencyId: participants[proxyObligation.initiatingFspProxyOrParticipantId.proxyId].participantCurrencyId,
502
+ transferParticipantRoleTypeId: Enum.Accounts.TransferParticipantRoleType.PAYER_DFSP,
503
+ ledgerEntryTypeId: Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE,
504
+ amount: -payload.amount.amount,
505
+ externalParticipantId
506
+ }
507
+ } else {
508
+ payerTransferParticipantRecord = {
509
+ transferId: payload.transferId,
510
+ participantId: participants[payload.payerFsp].id,
511
+ participantCurrencyId: participants[payload.payerFsp].participantCurrencyId,
512
+ transferParticipantRoleTypeId: Enum.Accounts.TransferParticipantRoleType.PAYER_DFSP,
513
+ ledgerEntryTypeId: Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE,
514
+ amount: payload.amount.amount
515
+ }
451
516
  }
452
517
 
453
- const payeeTransferParticipantRecord = {
454
- transferId: payload.transferId,
455
- participantCurrencyId: participantCurrencyIds[payload.payeeFsp],
456
- transferParticipantRoleTypeId: Enum.Accounts.TransferParticipantRoleType.PAYEE_DFSP,
457
- ledgerEntryTypeId: Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE,
458
- amount: -payload.amount.amount
518
+ logger.debug('saveTransferPrepared participants:', { participants })
519
+ let payeeTransferParticipantRecord
520
+ if (proxyObligation?.isCounterPartyFspProxy) {
521
+ const externalParticipantId = await ParticipantFacade.getExternalParticipantIdByNameOrCreate(proxyObligation.counterPartyFspProxyOrParticipantId)
522
+ // todo: think, what if externalParticipantId is null?
523
+ payeeTransferParticipantRecord = {
524
+ transferId: payload.transferId,
525
+ participantId: participants[proxyObligation.counterPartyFspProxyOrParticipantId.proxyId].id,
526
+ participantCurrencyId: null,
527
+ transferParticipantRoleTypeId: Enum.Accounts.TransferParticipantRoleType.PAYEE_DFSP,
528
+ ledgerEntryTypeId: Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE,
529
+ amount: -payload.amount.amount,
530
+ externalParticipantId
531
+ }
532
+ } else {
533
+ payeeTransferParticipantRecord = {
534
+ transferId: payload.transferId,
535
+ participantId: participants[payload.payeeFsp].id,
536
+ participantCurrencyId: participants[payload.payeeFsp].participantCurrencyId,
537
+ transferParticipantRoleTypeId: Enum.Accounts.TransferParticipantRoleType.PAYEE_DFSP,
538
+ ledgerEntryTypeId: Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE,
539
+ amount: -payload.amount.amount
540
+ }
459
541
  }
460
542
 
461
543
  const knex = await Db.getKnex()
@@ -485,10 +567,8 @@ const saveTransferPrepared = async (payload, stateReason = null, hasPassedValida
485
567
  }
486
568
  await knex('ilpPacket').transacting(trx).insert(ilpPacketRecord)
487
569
  await knex('transferStateChange').transacting(trx).insert(transferStateChangeRecord)
488
- await trx.commit()
489
570
  histTimerSaveTranferTransactionValidationPassedEnd({ success: true, queryName: 'facade_saveTransferPrepared_transaction' })
490
571
  } catch (err) {
491
- await trx.rollback()
492
572
  histTimerSaveTranferTransactionValidationPassedEnd({ success: false, queryName: 'facade_saveTransferPrepared_transaction' })
493
573
  throw err
494
574
  }
@@ -503,14 +583,14 @@ const saveTransferPrepared = async (payload, stateReason = null, hasPassedValida
503
583
  try {
504
584
  await knex('transferParticipant').insert(payerTransferParticipantRecord)
505
585
  } catch (err) {
506
- Logger.isWarnEnabled && Logger.warn(`Payer transferParticipant insert error: ${err.message}`)
586
+ logger.warn('Payer transferParticipant insert error', err)
507
587
  histTimerSaveTranferNoValidationEnd({ success: false, queryName: 'facade_saveTransferPrepared_no_validation' })
508
588
  }
509
589
  try {
510
590
  await knex('transferParticipant').insert(payeeTransferParticipantRecord)
511
591
  } catch (err) {
592
+ logger.warn('Payee transferParticipant insert error:', err)
512
593
  histTimerSaveTranferNoValidationEnd({ success: false, queryName: 'facade_saveTransferPrepared_no_validation' })
513
- Logger.isWarnEnabled && Logger.warn(`Payee transferParticipant insert error: ${err.message}`)
514
594
  }
515
595
  payerTransferParticipantRecord.name = payload.payerFsp
516
596
  payeeTransferParticipantRecord.name = payload.payeeFsp
@@ -526,26 +606,27 @@ const saveTransferPrepared = async (payload, stateReason = null, hasPassedValida
526
606
  try {
527
607
  await knex.batchInsert('transferExtension', transferExtensionsRecordList)
528
608
  } catch (err) {
529
- Logger.isWarnEnabled && Logger.warn(`batchInsert transferExtension error: ${err.message}`)
609
+ logger.warn('batchInsert transferExtension error:', err)
530
610
  histTimerSaveTranferNoValidationEnd({ success: false, queryName: 'facade_saveTransferPrepared_no_validation' })
531
611
  }
532
612
  }
533
613
  try {
534
614
  await knex('ilpPacket').insert(ilpPacketRecord)
535
615
  } catch (err) {
536
- Logger.isWarnEnabled && Logger.warn(`ilpPacket insert error: ${err.message}`)
616
+ logger.warn('ilpPacket insert error:', err)
537
617
  histTimerSaveTranferNoValidationEnd({ success: false, queryName: 'facade_saveTransferPrepared_no_validation' })
538
618
  }
539
619
  try {
540
620
  await knex('transferStateChange').insert(transferStateChangeRecord)
541
621
  histTimerSaveTranferNoValidationEnd({ success: true, queryName: 'facade_saveTransferPrepared_no_validation' })
542
622
  } catch (err) {
543
- Logger.isWarnEnabled && Logger.warn(`transferStateChange insert error: ${err.message}`)
623
+ logger.warn('transferStateChange insert error:', err)
544
624
  histTimerSaveTranferNoValidationEnd({ success: false, queryName: 'facade_saveTransferPrepared_no_validation' })
545
625
  }
546
626
  }
547
627
  histTimerSaveTransferPreparedEnd({ success: true, queryName: 'transfer_model_facade_saveTransferPrepared' })
548
628
  } catch (err) {
629
+ logger.warn('error in saveTransferPrepared', err)
549
630
  histTimerSaveTransferPreparedEnd({ success: false, queryName: 'transfer_model_facade_saveTransferPrepared' })
550
631
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
551
632
  }
@@ -592,7 +673,265 @@ const getTransferStateByTransferId = async (id) => {
592
673
  }
593
674
  }
594
675
 
595
- const timeoutExpireReserved = async (segmentId, intervalMin, intervalMax) => {
676
+ const _processTimeoutEntries = async (knex, trx, transactionTimestamp) => {
677
+ // Insert `transferStateChange` records for RECEIVED_PREPARE
678
+ await knex.from(knex.raw('transferStateChange (transferId, transferStateId, reason)')).transacting(trx)
679
+ .insert(function () {
680
+ this.from('transferTimeout AS tt')
681
+ .innerJoin(knex('transferStateChange AS tsc1')
682
+ .select('tsc1.transferId')
683
+ .max('tsc1.transferStateChangeId AS maxTransferStateChangeId')
684
+ .innerJoin('transferTimeout AS tt1', 'tt1.transferId', 'tsc1.transferId')
685
+ .groupBy('tsc1.transferId').as('ts'), 'ts.transferId', 'tt.transferId'
686
+ )
687
+ .innerJoin('transferStateChange AS tsc', 'tsc.transferStateChangeId', 'ts.maxTransferStateChangeId')
688
+ .where('tt.expirationDate', '<', transactionTimestamp)
689
+ .andWhere('tsc.transferStateId', `${Enum.Transfers.TransferInternalState.RECEIVED_PREPARE}`)
690
+ .select('tt.transferId', knex.raw('?', Enum.Transfers.TransferInternalState.EXPIRED_PREPARED), knex.raw('?', 'Aborted by Timeout Handler'))
691
+ })
692
+
693
+ // Insert `transferStateChange` records for RESERVED
694
+ await knex.from(knex.raw('transferStateChange (transferId, transferStateId, reason)')).transacting(trx)
695
+ .insert(function () {
696
+ this.from('transferTimeout AS tt')
697
+ .innerJoin(knex('transferStateChange AS tsc1')
698
+ .select('tsc1.transferId')
699
+ .max('tsc1.transferStateChangeId AS maxTransferStateChangeId')
700
+ .innerJoin('transferTimeout AS tt1', 'tt1.transferId', 'tsc1.transferId')
701
+ .groupBy('tsc1.transferId').as('ts'), 'ts.transferId', 'tt.transferId'
702
+ )
703
+ .innerJoin('transferStateChange AS tsc', 'tsc.transferStateChangeId', 'ts.maxTransferStateChangeId')
704
+ .where('tt.expirationDate', '<', transactionTimestamp)
705
+ .andWhere('tsc.transferStateId', `${Enum.Transfers.TransferState.RESERVED}`)
706
+ .select('tt.transferId', knex.raw('?', Enum.Transfers.TransferInternalState.RESERVED_TIMEOUT), knex.raw('?', 'Marked for expiration by Timeout Handler'))
707
+ })
708
+ }
709
+
710
+ const _insertTransferErrorEntries = async (knex, trx, transactionTimestamp) => {
711
+ // Insert `transferError` records
712
+ await knex.from(knex.raw('transferError (transferId, transferStateChangeId, errorCode, errorDescription)')).transacting(trx)
713
+ .insert(function () {
714
+ this.from('transferTimeout AS tt')
715
+ .innerJoin(knex('transferStateChange AS tsc1')
716
+ .select('tsc1.transferId')
717
+ .max('tsc1.transferStateChangeId AS maxTransferStateChangeId')
718
+ .innerJoin('transferTimeout AS tt1', 'tt1.transferId', 'tsc1.transferId')
719
+ .groupBy('tsc1.transferId').as('ts'), 'ts.transferId', 'tt.transferId'
720
+ )
721
+ .innerJoin('transferStateChange AS tsc', 'tsc.transferStateChangeId', 'ts.maxTransferStateChangeId')
722
+ .where('tt.expirationDate', '<', transactionTimestamp)
723
+ .andWhere('tsc.transferStateId', `${Enum.Transfers.TransferInternalState.RESERVED_TIMEOUT}`)
724
+ .select('tt.transferId', 'tsc.transferStateChangeId', knex.raw('?', ErrorHandler.Enums.FSPIOPErrorCodes.TRANSFER_EXPIRED.code), knex.raw('?', ErrorHandler.Enums.FSPIOPErrorCodes.TRANSFER_EXPIRED.message))
725
+ })
726
+ }
727
+
728
+ const _processFxTimeoutEntries = async (knex, trx, transactionTimestamp) => {
729
+ // Insert `fxTransferStateChange` records for RECEIVED_PREPARE
730
+ /* istanbul ignore next */
731
+ await knex.from(knex.raw('fxTransferStateChange (commitRequestId, transferStateId, reason)')).transacting(trx)
732
+ .insert(function () {
733
+ this.from('fxTransferTimeout AS ftt')
734
+ .innerJoin(knex('fxTransferStateChange AS ftsc1')
735
+ .select('ftsc1.commitRequestId')
736
+ .max('ftsc1.fxTransferStateChangeId AS maxFxTransferStateChangeId')
737
+ .innerJoin('fxTransferTimeout AS ftt1', 'ftt1.commitRequestId', 'ftsc1.commitRequestId')
738
+ .groupBy('ftsc1.commitRequestId').as('fts'), 'fts.commitRequestId', 'ftt.commitRequestId'
739
+ )
740
+ .innerJoin('fxTransferStateChange AS ftsc', 'ftsc.fxTransferStateChangeId', 'fts.maxFxTransferStateChangeId')
741
+ .where('ftt.expirationDate', '<', transactionTimestamp)
742
+ .andWhere('ftsc.transferStateId', `${Enum.Transfers.TransferInternalState.RECEIVED_PREPARE}`)
743
+ .select('ftt.commitRequestId', knex.raw('?', Enum.Transfers.TransferInternalState.EXPIRED_PREPARED), knex.raw('?', 'Aborted by Timeout Handler'))
744
+ })
745
+
746
+ // Insert `fxTransferStateChange` records for RESERVED
747
+ await knex.from(knex.raw('fxTransferStateChange (commitRequestId, transferStateId, reason)')).transacting(trx)
748
+ .insert(function () {
749
+ this.from('fxTransferTimeout AS ftt')
750
+ .innerJoin(knex('fxTransferStateChange AS ftsc1')
751
+ .select('ftsc1.commitRequestId')
752
+ .max('ftsc1.fxTransferStateChangeId AS maxFxTransferStateChangeId')
753
+ .innerJoin('fxTransferTimeout AS ftt1', 'ftt1.commitRequestId', 'ftsc1.commitRequestId')
754
+ .groupBy('ftsc1.commitRequestId').as('fts'), 'fts.commitRequestId', 'ftt.commitRequestId'
755
+ )
756
+ .innerJoin('fxTransferStateChange AS ftsc', 'ftsc.fxTransferStateChangeId', 'fts.maxFxTransferStateChangeId')
757
+ .where('ftt.expirationDate', '<', transactionTimestamp)
758
+ .andWhere('ftsc.transferStateId', `${Enum.Transfers.TransferState.RESERVED}`)
759
+ .select('ftt.commitRequestId', knex.raw('?', Enum.Transfers.TransferInternalState.RESERVED_TIMEOUT), knex.raw('?', 'Marked for expiration by Timeout Handler'))
760
+ })
761
+
762
+ // Insert `fxTransferStateChange` records for RECEIVED_FULFIL_DEPENDENT
763
+ await knex.from(knex.raw('fxTransferStateChange (commitRequestId, transferStateId, reason)')).transacting(trx)
764
+ .insert(function () {
765
+ this.from('fxTransferTimeout AS ftt')
766
+ .innerJoin(knex('fxTransferStateChange AS ftsc1')
767
+ .select('ftsc1.commitRequestId')
768
+ .max('ftsc1.fxTransferStateChangeId AS maxFxTransferStateChangeId')
769
+ .innerJoin('fxTransferTimeout AS ftt1', 'ftt1.commitRequestId', 'ftsc1.commitRequestId')
770
+ .groupBy('ftsc1.commitRequestId').as('fts'), 'fts.commitRequestId', 'ftt.commitRequestId'
771
+ )
772
+ .innerJoin('fxTransferStateChange AS ftsc', 'ftsc.fxTransferStateChangeId', 'fts.maxFxTransferStateChangeId')
773
+ .where('ftt.expirationDate', '<', transactionTimestamp)
774
+ .andWhere('ftsc.transferStateId', `${Enum.Transfers.TransferInternalState.RECEIVED_FULFIL_DEPENDENT}`)
775
+ .select('ftt.commitRequestId', knex.raw('?', Enum.Transfers.TransferInternalState.RESERVED_TIMEOUT), knex.raw('?', 'Marked for expiration by Timeout Handler'))
776
+ })
777
+ }
778
+
779
+ const _insertFxTransferErrorEntries = async (knex, trx, transactionTimestamp) => {
780
+ // Insert `fxTransferError` records
781
+ await knex.from(knex.raw('fxTransferError (commitRequestId, fxTransferStateChangeId, errorCode, errorDescription)')).transacting(trx)
782
+ .insert(function () {
783
+ this.from('fxTransferTimeout AS ftt')
784
+ .innerJoin(knex('fxTransferStateChange AS ftsc1')
785
+ .select('ftsc1.commitRequestId')
786
+ .max('ftsc1.fxTransferStateChangeId AS maxFxTransferStateChangeId')
787
+ .innerJoin('fxTransferTimeout AS ftt1', 'ftt1.commitRequestId', 'ftsc1.commitRequestId')
788
+ .groupBy('ftsc1.commitRequestId').as('fts'), 'fts.commitRequestId', 'ftt.commitRequestId'
789
+ )
790
+ .innerJoin('fxTransferStateChange AS ftsc', 'ftsc.fxTransferStateChangeId', 'fts.maxFxTransferStateChangeId')
791
+ .where('ftt.expirationDate', '<', transactionTimestamp)
792
+ .andWhere('ftsc.transferStateId', `${Enum.Transfers.TransferInternalState.RESERVED_TIMEOUT}`)
793
+ .select('ftt.commitRequestId', 'ftsc.fxTransferStateChangeId', knex.raw('?', ErrorHandler.Enums.FSPIOPErrorCodes.TRANSFER_EXPIRED.code), knex.raw('?', ErrorHandler.Enums.FSPIOPErrorCodes.TRANSFER_EXPIRED.message))
794
+ })
795
+ }
796
+
797
+ const _getTransferTimeoutList = async (knex, transactionTimestamp) => {
798
+ /* istanbul ignore next */
799
+ return knex('transferTimeout AS tt')
800
+ .innerJoin(knex('transferStateChange AS tsc1')
801
+ .select('tsc1.transferId')
802
+ .max('tsc1.transferStateChangeId AS maxTransferStateChangeId')
803
+ .innerJoin('transferTimeout AS tt1', 'tt1.transferId', 'tsc1.transferId')
804
+ .groupBy('tsc1.transferId')
805
+ .as('ts'), 'ts.transferId', 'tt.transferId'
806
+ )
807
+ .innerJoin('transferStateChange AS tsc', 'tsc.transferStateChangeId', 'ts.maxTransferStateChangeId')
808
+ .innerJoin('transferParticipant AS tp1', function () {
809
+ this.on('tp1.transferId', 'tt.transferId')
810
+ .andOn('tp1.transferParticipantRoleTypeId', Enum.Accounts.TransferParticipantRoleType.PAYER_DFSP)
811
+ .andOn('tp1.ledgerEntryTypeId', Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE)
812
+ })
813
+ .leftJoin('externalParticipant AS ep1', 'ep1.externalParticipantId', 'tp1.externalParticipantId')
814
+ .innerJoin('transferParticipant AS tp2', function () {
815
+ this.on('tp2.transferId', 'tt.transferId')
816
+ .andOn('tp2.transferParticipantRoleTypeId', Enum.Accounts.TransferParticipantRoleType.PAYEE_DFSP)
817
+ .andOn('tp2.ledgerEntryTypeId', Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE)
818
+ })
819
+ .leftJoin('externalParticipant AS ep2', 'ep2.externalParticipantId', 'tp2.externalParticipantId')
820
+ .innerJoin('participant AS p1', 'p1.participantId', 'tp1.participantId')
821
+ .innerJoin('participant AS p2', 'p2.participantId', 'tp2.participantId')
822
+ .innerJoin(knex('transferStateChange AS tsc2')
823
+ .select('tsc2.transferId', 'tsc2.transferStateChangeId', 'ppc1.participantCurrencyId')
824
+ .innerJoin('transferTimeout AS tt2', 'tt2.transferId', 'tsc2.transferId')
825
+ .innerJoin('participantPositionChange AS ppc1', 'ppc1.transferStateChangeId', 'tsc2.transferStateChangeId')
826
+ .as('tpc'), 'tpc.transferId', 'tt.transferId'
827
+ )
828
+ .leftJoin('bulkTransferAssociation AS bta', 'bta.transferId', 'tt.transferId')
829
+
830
+ .where('tt.expirationDate', '<', transactionTimestamp)
831
+ .select(
832
+ 'tt.*',
833
+ 'tsc.transferStateId',
834
+ 'tp1.participantCurrencyId AS payerParticipantCurrencyId',
835
+ 'p1.name AS payerFsp',
836
+ 'p2.name AS payeeFsp',
837
+ 'tp2.participantCurrencyId AS payeeParticipantCurrencyId',
838
+ 'bta.bulkTransferId',
839
+ 'tpc.participantCurrencyId AS effectedParticipantCurrencyId',
840
+ 'ep1.name AS externalPayerName',
841
+ 'ep2.name AS externalPayeeName'
842
+ )
843
+ }
844
+
845
+ const _getFxTransferTimeoutList = async (knex, transactionTimestamp) => {
846
+ /* istanbul ignore next */
847
+ return knex('fxTransferTimeout AS ftt')
848
+ .innerJoin(knex('fxTransferStateChange AS ftsc1')
849
+ .select('ftsc1.commitRequestId')
850
+ .max('ftsc1.fxTransferStateChangeId AS maxFxTransferStateChangeId')
851
+ .innerJoin('fxTransferTimeout AS ftt1', 'ftt1.commitRequestId', 'ftsc1.commitRequestId')
852
+ .groupBy('ftsc1.commitRequestId')
853
+ .as('fts'), 'fts.commitRequestId', 'ftt.commitRequestId'
854
+ )
855
+ .innerJoin('fxTransferStateChange AS ftsc', 'ftsc.fxTransferStateChangeId', 'fts.maxFxTransferStateChangeId')
856
+ .innerJoin('fxTransferParticipant AS ftp1', function () {
857
+ this.on('ftp1.commitRequestId', 'ftt.commitRequestId')
858
+ .andOn('ftp1.transferParticipantRoleTypeId', Enum.Accounts.TransferParticipantRoleType.INITIATING_FSP)
859
+ .andOn('ftp1.ledgerEntryTypeId', Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE)
860
+ })
861
+ .leftJoin('externalParticipant AS ep1', 'ep1.externalParticipantId', 'ftp1.externalParticipantId')
862
+ .innerJoin('fxTransferParticipant AS ftp2', function () {
863
+ this.on('ftp2.commitRequestId', 'ftt.commitRequestId')
864
+ .andOn('ftp2.transferParticipantRoleTypeId', Enum.Accounts.TransferParticipantRoleType.COUNTER_PARTY_FSP)
865
+ .andOn('ftp2.fxParticipantCurrencyTypeId', Enum.Fx.FxParticipantCurrencyType.TARGET)
866
+ .andOn('ftp2.ledgerEntryTypeId', Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE)
867
+ })
868
+ .leftJoin('externalParticipant AS ep2', 'ep2.externalParticipantId', 'ftp2.externalParticipantId')
869
+ .innerJoin('participant AS p1', 'p1.participantId', 'ftp1.participantId')
870
+ .innerJoin('participant AS p2', 'p2.participantId', 'ftp2.participantId')
871
+ .innerJoin(knex('fxTransferStateChange AS ftsc2')
872
+ .select('ftsc2.commitRequestId', 'ftsc2.fxTransferStateChangeId', 'ppc1.participantCurrencyId')
873
+ .innerJoin('fxTransferTimeout AS ftt2', 'ftt2.commitRequestId', 'ftsc2.commitRequestId')
874
+ .innerJoin('participantPositionChange AS ppc1', 'ppc1.fxTransferStateChangeId', 'ftsc2.fxTransferStateChangeId')
875
+ .as('ftpc'), 'ftpc.commitRequestId', 'ftt.commitRequestId'
876
+ )
877
+ .where('ftt.expirationDate', '<', transactionTimestamp)
878
+ .select(
879
+ 'ftt.*',
880
+ 'ftsc.transferStateId',
881
+ 'ftp1.participantCurrencyId AS initiatingParticipantCurrencyId',
882
+ 'p1.name AS initiatingFsp',
883
+ 'p2.name AS counterPartyFsp',
884
+ 'ftp2.participantCurrencyId AS counterPartyParticipantCurrencyId',
885
+ 'ftpc.participantCurrencyId AS effectedParticipantCurrencyId',
886
+ 'ep1.name AS externalInitiatingFspName',
887
+ 'ep2.name AS externalCounterPartyFspName'
888
+ )
889
+ }
890
+
891
+ /**
892
+ * @typedef {Object} TimedOutTransfer
893
+ *
894
+ * @property {Integer} transferTimeoutId
895
+ * @property {String} transferId
896
+ * @property {Date} expirationDate
897
+ * @property {Date} createdDate
898
+ * @property {String} transferStateId
899
+ * @property {String} payerFsp
900
+ * @property {String} payeeFsp
901
+ * @property {Integer} payerParticipantCurrencyId
902
+ * @property {Integer} payeeParticipantCurrencyId
903
+ * @property {Integer} bulkTransferId
904
+ * @property {Integer} effectedParticipantCurrencyId
905
+ * @property {String} externalPayerName
906
+ * @property {String} externalPayeeName
907
+ */
908
+
909
+ /**
910
+ * @typedef {Object} TimedOutFxTransfer
911
+ *
912
+ * @property {Integer} fxTransferTimeoutId
913
+ * @property {String} commitRequestId
914
+ * @property {Date} expirationDate
915
+ * @property {Date} createdDate
916
+ * @property {String} transferStateId
917
+ * @property {String} initiatingFsp
918
+ * @property {String} counterPartyFsp
919
+ * @property {Integer} initiatingParticipantCurrencyId
920
+ * @property {Integer} counterPartyParticipantCurrencyId
921
+ * @property {Integer} effectedParticipantCurrencyId
922
+ * @property {String} externalInitiatingFspName
923
+ * @property {String} externalCounterPartyFspName
924
+ */
925
+
926
+ /**
927
+ * Returns the list of transfers/fxTransfers that have timed out
928
+ *
929
+ * @returns {Promise<{
930
+ * transferTimeoutList: TimedOutTransfer,
931
+ * fxTransferTimeoutList: TimedOutFxTransfer
932
+ * }>}
933
+ */
934
+ const timeoutExpireReserved = async (segmentId, intervalMin, intervalMax, fxSegmentId, fxIntervalMin, fxIntervalMax) => {
596
935
  try {
597
936
  const transactionTimestamp = Time.getUTCString(new Date())
598
937
  const knex = await Db.getKnex()
@@ -607,66 +946,129 @@ const timeoutExpireReserved = async (segmentId, intervalMin, intervalMax) => {
607
946
  .max('transferStateChangeId AS maxTransferStateChangeId')
608
947
  .where('transferStateChangeId', '>', intervalMin)
609
948
  .andWhere('transferStateChangeId', '<=', intervalMax)
610
- .groupBy('transferId').as('ts'), 'ts.transferId', 't.transferId'
949
+ .groupBy('transferId')
950
+ .as('ts'), 'ts.transferId', 't.transferId'
611
951
  )
612
952
  .innerJoin('transferStateChange AS tsc', 'tsc.transferStateChangeId', 'ts.maxTransferStateChangeId')
613
953
  .leftJoin('transferTimeout AS tt', 'tt.transferId', 't.transferId')
614
954
  .whereNull('tt.transferId')
615
955
  .whereIn('tsc.transferStateId', [`${Enum.Transfers.TransferInternalState.RECEIVED_PREPARE}`, `${Enum.Transfers.TransferState.RESERVED}`])
616
956
  .select('t.transferId', 't.expirationDate')
617
- }) // .toSQL().sql
618
- // console.log('SQL: ' + q1)
957
+ })
619
958
 
620
- // Insert `transferStateChange` records for RECEIVED_PREPARE
621
- await knex.from(knex.raw('transferStateChange (transferId, transferStateId, reason)')).transacting(trx)
959
+ // Insert `fxTransferTimeout` records for fxTransfers found between the interval intervalMin <= intervalMax and related fxTransfers
960
+ await knex.from(knex.raw('fxTransferTimeout (commitRequestId, expirationDate)')).transacting(trx)
622
961
  .insert(function () {
623
- this.from('transferTimeout AS tt')
624
- .innerJoin(knex('transferStateChange AS tsc1')
625
- .select('tsc1.transferId')
626
- .max('tsc1.transferStateChangeId AS maxTransferStateChangeId')
627
- .innerJoin('transferTimeout AS tt1', 'tt1.transferId', 'tsc1.transferId')
628
- .groupBy('tsc1.transferId').as('ts'), 'ts.transferId', 'tt.transferId'
962
+ this.from('fxTransfer AS ft')
963
+ .innerJoin(knex('fxTransferStateChange')
964
+ .select('commitRequestId')
965
+ .max('fxTransferStateChangeId AS maxFxTransferStateChangeId')
966
+ .where('fxTransferStateChangeId', '>', fxIntervalMin)
967
+ .andWhere('fxTransferStateChangeId', '<=', fxIntervalMax)
968
+ .groupBy('commitRequestId').as('fts'), 'fts.commitRequestId', 'ft.commitRequestId'
629
969
  )
630
- .innerJoin('transferStateChange AS tsc', 'tsc.transferStateChangeId', 'ts.maxTransferStateChangeId')
631
- .where('tt.expirationDate', '<', transactionTimestamp)
632
- .andWhere('tsc.transferStateId', `${Enum.Transfers.TransferInternalState.RECEIVED_PREPARE}`)
633
- .select('tt.transferId', knex.raw('?', Enum.Transfers.TransferInternalState.EXPIRED_PREPARED), knex.raw('?', 'Aborted by Timeout Handler'))
634
- }) // .toSQL().sql
635
- // console.log('SQL: ' + q2)
636
-
637
- // Insert `transferStateChange` records for RESERVED
638
- await knex.from(knex.raw('transferStateChange (transferId, transferStateId, reason)')).transacting(trx)
970
+ .innerJoin('fxTransferStateChange AS ftsc', 'ftsc.fxTransferStateChangeId', 'fts.maxFxTransferStateChangeId')
971
+ .leftJoin('fxTransferTimeout AS ftt', 'ftt.commitRequestId', 'ft.commitRequestId')
972
+ .leftJoin('fxTransfer AS ft1', 'ft1.determiningTransferId', 'ft.determiningTransferId')
973
+ .whereNull('ftt.commitRequestId')
974
+ .whereIn('ftsc.transferStateId', [
975
+ `${Enum.Transfers.TransferInternalState.RECEIVED_PREPARE}`,
976
+ `${Enum.Transfers.TransferState.RESERVED}`,
977
+ `${Enum.Transfers.TransferInternalState.RECEIVED_FULFIL_DEPENDENT}`
978
+ ]) // TODO: this needs to be updated to proper states for fx
979
+ .select('ft1.commitRequestId', 'ft.expirationDate') // Passing expiration date of the timed out fxTransfer for all related fxTransfers
980
+ })
981
+
982
+ await _processTimeoutEntries(knex, trx, transactionTimestamp)
983
+ await _processFxTimeoutEntries(knex, trx, transactionTimestamp)
984
+
985
+ // Insert `fxTransferTimeout` records for the related fxTransfers, or update if exists. The expiration date will be of the transfer and not from fxTransfer
986
+ await knex.from(knex.raw('fxTransferTimeout (commitRequestId, expirationDate)')).transacting(trx)
639
987
  .insert(function () {
640
- this.from('transferTimeout AS tt')
641
- .innerJoin(knex('transferStateChange AS tsc1')
642
- .select('tsc1.transferId')
643
- .max('tsc1.transferStateChangeId AS maxTransferStateChangeId')
644
- .innerJoin('transferTimeout AS tt1', 'tt1.transferId', 'tsc1.transferId')
645
- .groupBy('tsc1.transferId').as('ts'), 'ts.transferId', 'tt.transferId'
988
+ this.from('fxTransfer AS ft')
989
+ .innerJoin(
990
+ knex('transferTimeout AS tt')
991
+ .select('tt.transferId', 'tt.expirationDate')
992
+ .innerJoin(
993
+ knex('transferStateChange as tsc1')
994
+ .select('tsc1.transferId')
995
+ .max('tsc1.transferStateChangeId AS maxTransferStateChangeId')
996
+ .innerJoin('transferTimeout AS tt1', 'tt1.transferId', 'tsc1.transferId')
997
+ .groupBy('tsc1.transferId')
998
+ .as('ts'),
999
+ 'ts.transferId', 'tt.transferId'
1000
+ )
1001
+ .innerJoin('transferStateChange AS tsc', 'tsc.transferStateChangeId', 'ts.maxTransferStateChangeId')
1002
+ .where('tt.expirationDate', '<', transactionTimestamp)
1003
+ .whereIn('tsc.transferStateId', [
1004
+ `${Enum.Transfers.TransferInternalState.RESERVED_TIMEOUT}`,
1005
+ `${Enum.Transfers.TransferInternalState.EXPIRED_PREPARED}`
1006
+ ])
1007
+ .as('tt1'),
1008
+ 'ft.determiningTransferId', 'tt1.transferId'
646
1009
  )
647
- .innerJoin('transferStateChange AS tsc', 'tsc.transferStateChangeId', 'ts.maxTransferStateChangeId')
648
- .where('tt.expirationDate', '<', transactionTimestamp)
649
- .andWhere('tsc.transferStateId', `${Enum.Transfers.TransferState.RESERVED}`)
650
- .select('tt.transferId', knex.raw('?', Enum.Transfers.TransferInternalState.RESERVED_TIMEOUT), knex.raw('?', 'Marked for expiration by Timeout Handler'))
651
- }) // .toSQL().sql
652
- // console.log('SQL: ' + q3)
653
-
654
- // Insert `transferError` records
655
- await knex.from(knex.raw('transferError (transferId, transferStateChangeId, errorCode, errorDescription)')).transacting(trx)
1010
+ .select('ft.commitRequestId', 'tt1.expirationDate')
1011
+ })
1012
+ .onConflict('commitRequestId')
1013
+ .merge({
1014
+ expirationDate: knex.raw('VALUES(expirationDate)')
1015
+ })
1016
+
1017
+ // Insert `transferTimeout` records for the related transfers, or update if exists. The expiration date will be of the fxTransfer and not from transfer
1018
+ await knex.from(knex.raw('transferTimeout (transferId, expirationDate)')).transacting(trx)
656
1019
  .insert(function () {
657
- this.from('transferTimeout AS tt')
658
- .innerJoin(knex('transferStateChange AS tsc1')
659
- .select('tsc1.transferId')
660
- .max('tsc1.transferStateChangeId AS maxTransferStateChangeId')
661
- .innerJoin('transferTimeout AS tt1', 'tt1.transferId', 'tsc1.transferId')
662
- .groupBy('tsc1.transferId').as('ts'), 'ts.transferId', 'tt.transferId'
1020
+ this.from('fxTransfer AS ft')
1021
+ .innerJoin(
1022
+ knex('fxTransferTimeout AS ftt')
1023
+ .select('ftt.commitRequestId', 'ftt.expirationDate')
1024
+ .innerJoin(
1025
+ knex('fxTransferStateChange AS ftsc1')
1026
+ .select('ftsc1.commitRequestId')
1027
+ .max('ftsc1.fxTransferStateChangeId AS maxFxTransferStateChangeId')
1028
+ .innerJoin('fxTransferTimeout AS ftt1', 'ftt1.commitRequestId', 'ftsc1.commitRequestId')
1029
+ .groupBy('ftsc1.commitRequestId')
1030
+ .as('fts'),
1031
+ 'fts.commitRequestId', 'ftt.commitRequestId'
1032
+ )
1033
+ .innerJoin('fxTransferStateChange AS ftsc', 'ftsc.fxTransferStateChangeId', 'fts.maxFxTransferStateChangeId')
1034
+ .where('ftt.expirationDate', '<', transactionTimestamp)
1035
+ .whereIn('ftsc.transferStateId', [
1036
+ `${Enum.Transfers.TransferInternalState.RESERVED_TIMEOUT}`,
1037
+ `${Enum.Transfers.TransferInternalState.EXPIRED_PREPARED}`
1038
+ ]) // TODO: need to check this for fx
1039
+ .as('ftt1'),
1040
+ 'ft.commitRequestId', 'ftt1.commitRequestId'
663
1041
  )
664
- .innerJoin('transferStateChange AS tsc', 'tsc.transferStateChangeId', 'ts.maxTransferStateChangeId')
665
- .where('tt.expirationDate', '<', transactionTimestamp)
666
- .andWhere('tsc.transferStateId', `${Enum.Transfers.TransferInternalState.RESERVED_TIMEOUT}`)
667
- .select('tt.transferId', 'tsc.transferStateChangeId', knex.raw('?', ErrorHandler.Enums.FSPIOPErrorCodes.TRANSFER_EXPIRED.code), knex.raw('?', ErrorHandler.Enums.FSPIOPErrorCodes.TRANSFER_EXPIRED.message))
668
- }) // .toSQL().sql
669
- // console.log('SQL: ' + q4)
1042
+ .innerJoin(
1043
+ knex('transferStateChange AS tsc')
1044
+ .select('tsc.transferId')
1045
+ .innerJoin(
1046
+ knex('transferStateChange AS tsc1')
1047
+ .select('tsc1.transferId')
1048
+ .max('tsc1.transferStateChangeId AS maxTransferStateChangeId')
1049
+ .groupBy('tsc1.transferId')
1050
+ .as('ts'),
1051
+ 'ts.transferId', 'tsc.transferId'
1052
+ )
1053
+ .whereRaw('tsc.transferStateChangeId = ts.maxTransferStateChangeId')
1054
+ .whereIn('tsc.transferStateId', [
1055
+ `${Enum.Transfers.TransferInternalState.RECEIVED_PREPARE}`,
1056
+ `${Enum.Transfers.TransferState.RESERVED}`
1057
+ ])
1058
+ .as('tt1'),
1059
+ 'ft.determiningTransferId', 'tt1.transferId'
1060
+ )
1061
+ .select('tt1.transferId', 'ftt1.expirationDate')
1062
+ })
1063
+ .onConflict('transferId')
1064
+ .merge({
1065
+ expirationDate: knex.raw('VALUES(expirationDate)')
1066
+ })
1067
+
1068
+ await _processTimeoutEntries(knex, trx, transactionTimestamp)
1069
+ await _processFxTimeoutEntries(knex, trx, transactionTimestamp)
1070
+ await _insertTransferErrorEntries(knex, trx, transactionTimestamp)
1071
+ await _insertFxTransferErrorEntries(knex, trx, transactionTimestamp)
670
1072
 
671
1073
  if (segmentId === 0) {
672
1074
  const segment = {
@@ -679,45 +1081,31 @@ const timeoutExpireReserved = async (segmentId, intervalMin, intervalMax) => {
679
1081
  } else {
680
1082
  await knex('segment').transacting(trx).where({ segmentId }).update({ value: intervalMax })
681
1083
  }
682
- await trx.commit
1084
+ if (fxSegmentId === 0) {
1085
+ const fxSegment = {
1086
+ segmentType: 'timeout',
1087
+ enumeration: 0,
1088
+ tableName: 'fxTransferStateChange',
1089
+ value: fxIntervalMax
1090
+ }
1091
+ await knex('segment').transacting(trx).insert(fxSegment)
1092
+ } else {
1093
+ await knex('segment').transacting(trx).where({ segmentId: fxSegmentId }).update({ value: fxIntervalMax })
1094
+ }
683
1095
  } catch (err) {
684
- await trx.rollback
685
1096
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
686
1097
  }
687
1098
  }).catch((err) => {
688
1099
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
689
1100
  })
690
1101
 
691
- return knex('transferTimeout AS tt')
692
- .innerJoin(knex('transferStateChange AS tsc1')
693
- .select('tsc1.transferId')
694
- .max('tsc1.transferStateChangeId AS maxTransferStateChangeId')
695
- .innerJoin('transferTimeout AS tt1', 'tt1.transferId', 'tsc1.transferId')
696
- .groupBy('tsc1.transferId').as('ts'), 'ts.transferId', 'tt.transferId'
697
- )
698
- .innerJoin('transferStateChange AS tsc', 'tsc.transferStateChangeId', 'ts.maxTransferStateChangeId')
699
- .innerJoin('transferParticipant AS tp1', function () {
700
- this.on('tp1.transferId', 'tt.transferId')
701
- .andOn('tp1.transferParticipantRoleTypeId', Enum.Accounts.TransferParticipantRoleType.PAYER_DFSP)
702
- .andOn('tp1.ledgerEntryTypeId', Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE)
703
- })
704
- .innerJoin('transferParticipant AS tp2', function () {
705
- this.on('tp2.transferId', 'tt.transferId')
706
- .andOn('tp2.transferParticipantRoleTypeId', Enum.Accounts.TransferParticipantRoleType.PAYEE_DFSP)
707
- .andOn('tp2.ledgerEntryTypeId', Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE)
708
- })
709
- .innerJoin('participantCurrency AS pc1', 'pc1.participantCurrencyId', 'tp1.participantCurrencyId')
710
- .innerJoin('participant AS p1', 'p1.participantId', 'pc1.participantId')
1102
+ const transferTimeoutList = await _getTransferTimeoutList(knex, transactionTimestamp)
1103
+ const fxTransferTimeoutList = await _getFxTransferTimeoutList(knex, transactionTimestamp)
711
1104
 
712
- .innerJoin('participantCurrency AS pc2', 'pc2.participantCurrencyId', 'tp2.participantCurrencyId')
713
- .innerJoin('participant AS p2', 'p2.participantId', 'pc2.participantId')
714
-
715
- .leftJoin('bulkTransferAssociation AS bta', 'bta.transferId', 'tt.transferId')
716
-
717
- .where('tt.expirationDate', '<', transactionTimestamp)
718
- .select('tt.*', 'tsc.transferStateId', 'tp1.participantCurrencyId AS payerParticipantCurrencyId',
719
- 'p1.name AS payerFsp', 'p2.name AS payeeFsp', 'tp2.participantCurrencyId AS payeeParticipantCurrencyId',
720
- 'bta.bulkTransferId')
1105
+ return {
1106
+ transferTimeoutList,
1107
+ fxTransferTimeoutList
1108
+ }
721
1109
  } catch (err) {
722
1110
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
723
1111
  }
@@ -727,119 +1115,113 @@ const transferStateAndPositionUpdate = async function (param1, enums, trx = null
727
1115
  try {
728
1116
  const knex = await Db.getKnex()
729
1117
 
730
- const trxFunction = async (trx, doCommit = true) => {
1118
+ const trxFunction = async (trx) => {
731
1119
  const transactionTimestamp = Time.getUTCString(new Date())
732
- let info, transferStateChangeId
733
- try {
734
- info = await knex('transfer AS t')
735
- .join('transferParticipant AS dr', function () {
736
- this.on('dr.transferId', 't.transferId')
737
- .andOn('dr.amount', '>', 0)
738
- })
739
- .join('participantCurrency AS drpc', 'drpc.participantCurrencyId', 'dr.participantCurrencyId')
740
- .join('participantPosition AS drp', 'drp.participantCurrencyId', 'dr.participantCurrencyId')
741
- .join('transferParticipant AS cr', function () {
742
- this.on('cr.transferId', 't.transferId')
743
- .andOn('cr.amount', '<', 0)
1120
+ const info = await knex('transfer AS t')
1121
+ .join('transferParticipant AS dr', function () {
1122
+ this.on('dr.transferId', 't.transferId')
1123
+ .andOn('dr.amount', '>', 0)
1124
+ })
1125
+ .join('participantCurrency AS drpc', 'drpc.participantCurrencyId', 'dr.participantCurrencyId')
1126
+ .join('participantPosition AS drp', 'drp.participantCurrencyId', 'dr.participantCurrencyId')
1127
+ .join('transferParticipant AS cr', function () {
1128
+ this.on('cr.transferId', 't.transferId')
1129
+ .andOn('cr.amount', '<', 0)
1130
+ })
1131
+ .join('participantCurrency AS crpc', 'crpc.participantCurrencyId', 'dr.participantCurrencyId')
1132
+ .join('participantPosition AS crp', 'crp.participantCurrencyId', 'cr.participantCurrencyId')
1133
+ .join('transferStateChange AS tsc', 'tsc.transferId', 't.transferId')
1134
+ .where('t.transferId', param1.transferId)
1135
+ .whereIn('drpc.ledgerAccountTypeId', [enums.ledgerAccountType.POSITION, enums.ledgerAccountType.SETTLEMENT,
1136
+ enums.ledgerAccountType.HUB_RECONCILIATION, enums.ledgerAccountType.HUB_MULTILATERAL_SETTLEMENT])
1137
+ .whereIn('crpc.ledgerAccountTypeId', [enums.ledgerAccountType.POSITION, enums.ledgerAccountType.SETTLEMENT,
1138
+ enums.ledgerAccountType.HUB_RECONCILIATION, enums.ledgerAccountType.HUB_MULTILATERAL_SETTLEMENT])
1139
+ .select('dr.participantCurrencyId AS drAccountId', 'dr.amount AS drAmount', 'drp.participantPositionId AS drPositionId',
1140
+ 'drp.value AS drPositionValue', 'drp.reservedValue AS drReservedValue', 'cr.participantCurrencyId AS crAccountId',
1141
+ 'cr.amount AS crAmount', 'crp.participantPositionId AS crPositionId', 'crp.value AS crPositionValue',
1142
+ 'crp.reservedValue AS crReservedValue', 'tsc.transferStateId', 'drpc.ledgerAccountTypeId', 'crpc.ledgerAccountTypeId')
1143
+ .orderBy('tsc.transferStateChangeId', 'desc')
1144
+ .first()
1145
+ .transacting(trx)
1146
+
1147
+ if (param1.transferStateId === enums.transferState.COMMITTED ||
1148
+ param1.transferStateId === TransferInternalState.RESERVED_FORWARDED
1149
+ ) {
1150
+ await knex('transferStateChange')
1151
+ .insert({
1152
+ transferId: param1.transferId,
1153
+ transferStateId: enums.transferState.RECEIVED_FULFIL,
1154
+ reason: param1.reason,
1155
+ createdDate: param1.createdDate
744
1156
  })
745
- .join('participantCurrency AS crpc', 'crpc.participantCurrencyId', 'dr.participantCurrencyId')
746
- .join('participantPosition AS crp', 'crp.participantCurrencyId', 'cr.participantCurrencyId')
747
- .join('transferStateChange AS tsc', 'tsc.transferId', 't.transferId')
748
- .where('t.transferId', param1.transferId)
749
- .whereIn('drpc.ledgerAccountTypeId', [enums.ledgerAccountType.POSITION, enums.ledgerAccountType.SETTLEMENT,
750
- enums.ledgerAccountType.HUB_RECONCILIATION, enums.ledgerAccountType.HUB_MULTILATERAL_SETTLEMENT])
751
- .whereIn('crpc.ledgerAccountTypeId', [enums.ledgerAccountType.POSITION, enums.ledgerAccountType.SETTLEMENT,
752
- enums.ledgerAccountType.HUB_RECONCILIATION, enums.ledgerAccountType.HUB_MULTILATERAL_SETTLEMENT])
753
- .select('dr.participantCurrencyId AS drAccountId', 'dr.amount AS drAmount', 'drp.participantPositionId AS drPositionId',
754
- 'drp.value AS drPositionValue', 'drp.reservedValue AS drReservedValue', 'cr.participantCurrencyId AS crAccountId',
755
- 'cr.amount AS crAmount', 'crp.participantPositionId AS crPositionId', 'crp.value AS crPositionValue',
756
- 'crp.reservedValue AS crReservedValue', 'tsc.transferStateId', 'drpc.ledgerAccountTypeId', 'crpc.ledgerAccountTypeId')
757
- .orderBy('tsc.transferStateChangeId', 'desc')
758
- .first()
759
1157
  .transacting(trx)
760
-
761
- if (param1.transferStateId === enums.transferState.COMMITTED) {
762
- await knex('transferStateChange')
763
- .insert({
764
- transferId: param1.transferId,
765
- transferStateId: enums.transferState.RECEIVED_FULFIL,
766
- reason: param1.reason,
767
- createdDate: param1.createdDate
768
- })
769
- .transacting(trx)
770
- } else if (param1.transferStateId === enums.transferState.ABORTED_REJECTED) {
771
- await knex('transferStateChange')
772
- .insert({
773
- transferId: param1.transferId,
774
- transferStateId: enums.transferState.RECEIVED_REJECT,
775
- reason: param1.reason,
776
- createdDate: param1.createdDate
777
- })
778
- .transacting(trx)
779
- }
780
- transferStateChangeId = await knex('transferStateChange')
1158
+ } else if (param1.transferStateId === enums.transferState.ABORTED_REJECTED) {
1159
+ await knex('transferStateChange')
781
1160
  .insert({
782
1161
  transferId: param1.transferId,
783
- transferStateId: param1.transferStateId,
1162
+ transferStateId: enums.transferState.RECEIVED_REJECT,
784
1163
  reason: param1.reason,
785
1164
  createdDate: param1.createdDate
786
1165
  })
787
1166
  .transacting(trx)
1167
+ }
1168
+ const transferStateChangeId = await knex('transferStateChange')
1169
+ .insert({
1170
+ transferId: param1.transferId,
1171
+ transferStateId: param1.transferStateId,
1172
+ reason: param1.reason,
1173
+ createdDate: param1.createdDate
1174
+ })
1175
+ .transacting(trx)
788
1176
 
789
- if (param1.drUpdated === true) {
790
- if (param1.transferStateId === 'ABORTED_REJECTED') {
791
- info.drAmount = -info.drAmount
792
- }
793
- await knex('participantPosition')
794
- .update({
795
- value: new MLNumber(info.drPositionValue).add(info.drAmount).toFixed(Config.AMOUNT.SCALE),
796
- changedDate: transactionTimestamp
797
- })
798
- .where('participantPositionId', info.drPositionId)
799
- .transacting(trx)
800
-
801
- await knex('participantPositionChange')
802
- .insert({
803
- participantPositionId: info.drPositionId,
804
- transferStateChangeId,
805
- value: new MLNumber(info.drPositionValue).add(info.drAmount).toFixed(Config.AMOUNT.SCALE),
806
- reservedValue: info.drReservedValue,
807
- createdDate: param1.createdDate
808
- })
809
- .transacting(trx)
1177
+ if (param1.drUpdated === true) {
1178
+ if (param1.transferStateId === 'ABORTED_REJECTED') {
1179
+ info.drAmount = -info.drAmount
810
1180
  }
1181
+ await knex('participantPosition')
1182
+ .update({
1183
+ value: new MLNumber(info.drPositionValue).add(info.drAmount).toFixed(Config.AMOUNT.SCALE),
1184
+ changedDate: transactionTimestamp
1185
+ })
1186
+ .where('participantPositionId', info.drPositionId)
1187
+ .transacting(trx)
811
1188
 
812
- if (param1.crUpdated === true) {
813
- if (param1.transferStateId === 'ABORTED_REJECTED') {
814
- info.crAmount = -info.crAmount
815
- }
816
- await knex('participantPosition')
817
- .update({
818
- value: new MLNumber(info.crPositionValue).add(info.crAmount).toFixed(Config.AMOUNT.SCALE),
819
- changedDate: transactionTimestamp
820
- })
821
- .where('participantPositionId', info.crPositionId)
822
- .transacting(trx)
823
-
824
- await knex('participantPositionChange')
825
- .insert({
826
- participantPositionId: info.crPositionId,
827
- transferStateChangeId,
828
- value: new MLNumber(info.crPositionValue).add(info.crAmount).toFixed(Config.AMOUNT.SCALE),
829
- reservedValue: info.crReservedValue,
830
- createdDate: param1.createdDate
831
- })
832
- .transacting(trx)
833
- }
1189
+ await knex('participantPositionChange')
1190
+ .insert({
1191
+ participantPositionId: info.drPositionId,
1192
+ participantCurrencyId: info.drAccountId,
1193
+ transferStateChangeId,
1194
+ value: new MLNumber(info.drPositionValue).add(info.drAmount).toFixed(Config.AMOUNT.SCALE),
1195
+ change: info.drAmount,
1196
+ reservedValue: info.drReservedValue,
1197
+ createdDate: param1.createdDate
1198
+ })
1199
+ .transacting(trx)
1200
+ }
834
1201
 
835
- if (doCommit) {
836
- await trx.commit
837
- }
838
- } catch (err) {
839
- if (doCommit) {
840
- await trx.rollback
1202
+ if (param1.crUpdated === true) {
1203
+ if (param1.transferStateId === 'ABORTED_REJECTED') {
1204
+ info.crAmount = -info.crAmount
841
1205
  }
842
- throw err
1206
+ await knex('participantPosition')
1207
+ .update({
1208
+ value: new MLNumber(info.crPositionValue).add(info.crAmount).toFixed(Config.AMOUNT.SCALE),
1209
+ changedDate: transactionTimestamp
1210
+ })
1211
+ .where('participantPositionId', info.crPositionId)
1212
+ .transacting(trx)
1213
+
1214
+ await knex('participantPositionChange')
1215
+ .insert({
1216
+ participantPositionId: info.crPositionId,
1217
+ participantCurrencyId: info.crAccountId,
1218
+ transferStateChangeId,
1219
+ value: new MLNumber(info.crPositionValue).add(info.crAmount).toFixed(Config.AMOUNT.SCALE),
1220
+ change: info.crAmount,
1221
+ reservedValue: info.crReservedValue,
1222
+ createdDate: param1.createdDate
1223
+ })
1224
+ .transacting(trx)
843
1225
  }
844
1226
  return {
845
1227
  transferStateChangeId,
@@ -849,7 +1231,7 @@ const transferStateAndPositionUpdate = async function (param1, enums, trx = null
849
1231
  }
850
1232
 
851
1233
  if (trx) {
852
- return await trxFunction(trx, false)
1234
+ return await trxFunction(trx)
853
1235
  } else {
854
1236
  return await knex.transaction(trxFunction)
855
1237
  }
@@ -858,115 +1240,128 @@ const transferStateAndPositionUpdate = async function (param1, enums, trx = null
858
1240
  }
859
1241
  }
860
1242
 
861
- const reconciliationTransferPrepare = async function (payload, transactionTimestamp, enums, trx = null) {
1243
+ const updatePrepareReservedForwarded = async function (transferId) {
862
1244
  try {
863
1245
  const knex = await Db.getKnex()
1246
+ return await knex('transferStateChange')
1247
+ .insert({
1248
+ transferId,
1249
+ transferStateId: TransferInternalState.RESERVED_FORWARDED,
1250
+ reason: null,
1251
+ createdDate: Time.getUTCString(new Date())
1252
+ })
1253
+ } catch (err) {
1254
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
1255
+ }
1256
+ }
864
1257
 
865
- const trxFunction = async (trx, doCommit = true) => {
866
- try {
867
- // transferDuplicateCheck check and insert is done prior to calling the prepare
868
- // see admin/handler.js :: transfer -> Comparators.duplicateCheckComparator
869
-
870
- // Insert transfer
871
- await knex('transfer')
872
- .insert({
873
- transferId: payload.transferId,
874
- amount: payload.amount.amount,
875
- currencyId: payload.amount.currency,
876
- ilpCondition: 0,
877
- expirationDate: Time.getUTCString(new Date(+new Date() +
878
- 1000 * Number(Config.INTERNAL_TRANSFER_VALIDITY_SECONDS))),
879
- createdDate: transactionTimestamp
880
- })
881
- .transacting(trx)
1258
+ const reconciliationTransferPrepare = async function (payload, transactionTimestamp, enums, trx = null) {
1259
+ try {
1260
+ const knex = await Db.getKnex()
882
1261
 
883
- // Retrieve hub reconciliation account for the specified currency
884
- const { reconciliationAccountId } = await knex('participantCurrency')
885
- .select('participantCurrencyId AS reconciliationAccountId')
886
- .where('participantId', Config.HUB_ID)
887
- .andWhere('currencyId', payload.amount.currency)
888
- .first()
889
- .transacting(trx)
1262
+ const trxFunction = async (trx) => {
1263
+ // transferDuplicateCheck check and insert is done prior to calling the prepare
1264
+ // see admin/handler.js :: transfer -> Comparators.duplicateCheckComparator
890
1265
 
891
- let ledgerEntryTypeId, amount
892
- if (payload.action === Enum.Transfers.AdminTransferAction.RECORD_FUNDS_IN) {
893
- ledgerEntryTypeId = enums.ledgerEntryType.RECORD_FUNDS_IN
894
- amount = payload.amount.amount
895
- } else if (payload.action === Enum.Transfers.AdminTransferAction.RECORD_FUNDS_OUT_PREPARE_RESERVE) {
896
- ledgerEntryTypeId = enums.ledgerEntryType.RECORD_FUNDS_OUT
897
- amount = -payload.amount.amount
898
- } else {
899
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, 'Action not allowed for reconciliationTransferPrepare')
900
- }
1266
+ // Insert transfer
1267
+ await knex('transfer')
1268
+ .insert({
1269
+ transferId: payload.transferId,
1270
+ amount: payload.amount.amount,
1271
+ currencyId: payload.amount.currency,
1272
+ ilpCondition: 0,
1273
+ expirationDate: Time.getUTCString(new Date(+new Date() +
1274
+ 1000 * Number(Config.INTERNAL_TRANSFER_VALIDITY_SECONDS))),
1275
+ createdDate: transactionTimestamp
1276
+ })
1277
+ .transacting(trx)
901
1278
 
902
- // Insert transferParticipant records
903
- await knex('transferParticipant')
904
- .insert({
905
- transferId: payload.transferId,
906
- participantCurrencyId: reconciliationAccountId,
907
- transferParticipantRoleTypeId: enums.transferParticipantRoleType.HUB,
908
- ledgerEntryTypeId,
909
- amount,
910
- createdDate: transactionTimestamp
911
- })
912
- .transacting(trx)
913
- await knex('transferParticipant')
914
- .insert({
915
- transferId: payload.transferId,
916
- participantCurrencyId: payload.participantCurrencyId,
917
- transferParticipantRoleTypeId: enums.transferParticipantRoleType.DFSP_SETTLEMENT,
918
- ledgerEntryTypeId,
919
- amount: -amount,
920
- createdDate: transactionTimestamp
921
- })
922
- .transacting(trx)
1279
+ // Retrieve hub reconciliation account for the specified currency
1280
+ const { reconciliationAccountId } = await knex('participantCurrency')
1281
+ .select('participantCurrencyId AS reconciliationAccountId')
1282
+ .where('participantId', Config.HUB_ID)
1283
+ .andWhere('currencyId', payload.amount.currency)
1284
+ .first()
1285
+ .transacting(trx)
923
1286
 
924
- await knex('transferStateChange')
925
- .insert({
926
- transferId: payload.transferId,
927
- transferStateId: enums.transferState.RECEIVED_PREPARE,
928
- reason: payload.reason,
929
- createdDate: transactionTimestamp
930
- })
931
- .transacting(trx)
1287
+ // Get participantId based on participantCurrencyId
1288
+ const { participantId } = await knex('participantCurrency')
1289
+ .select('participantId')
1290
+ .where('participantCurrencyId', payload.participantCurrencyId)
1291
+ .first()
1292
+ .transacting(trx)
1293
+
1294
+ let ledgerEntryTypeId, amount
1295
+ if (payload.action === Enum.Transfers.AdminTransferAction.RECORD_FUNDS_IN) {
1296
+ ledgerEntryTypeId = enums.ledgerEntryType.RECORD_FUNDS_IN
1297
+ amount = payload.amount.amount
1298
+ } else if (payload.action === Enum.Transfers.AdminTransferAction.RECORD_FUNDS_OUT_PREPARE_RESERVE) {
1299
+ ledgerEntryTypeId = enums.ledgerEntryType.RECORD_FUNDS_OUT
1300
+ amount = -payload.amount.amount
1301
+ } else {
1302
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, 'Action not allowed for reconciliationTransferPrepare')
1303
+ }
932
1304
 
933
- // Save transaction reference and transfer extensions
934
- let transferExtensions = []
935
- transferExtensions.push({
1305
+ // Insert transferParticipant records
1306
+ await knex('transferParticipant')
1307
+ .insert({
936
1308
  transferId: payload.transferId,
937
- key: 'externalReference',
938
- value: payload.externalReference,
1309
+ participantId: Config.HUB_ID,
1310
+ participantCurrencyId: reconciliationAccountId,
1311
+ transferParticipantRoleTypeId: enums.transferParticipantRoleType.HUB,
1312
+ ledgerEntryTypeId,
1313
+ amount,
939
1314
  createdDate: transactionTimestamp
940
1315
  })
941
- if (payload.extensionList && payload.extensionList.extension) {
942
- transferExtensions = transferExtensions.concat(
943
- payload.extensionList.extension.map(ext => {
944
- return {
945
- transferId: payload.transferId,
946
- key: ext.key,
947
- value: ext.value,
948
- createdDate: transactionTimestamp
949
- }
950
- })
951
- )
952
- }
953
- for (const transferExtension of transferExtensions) {
954
- await knex('transferExtension').insert(transferExtension).transacting(trx)
955
- }
1316
+ .transacting(trx)
1317
+ await knex('transferParticipant')
1318
+ .insert({
1319
+ transferId: payload.transferId,
1320
+ participantId,
1321
+ participantCurrencyId: payload.participantCurrencyId,
1322
+ transferParticipantRoleTypeId: enums.transferParticipantRoleType.DFSP_SETTLEMENT,
1323
+ ledgerEntryTypeId,
1324
+ amount: -amount,
1325
+ createdDate: transactionTimestamp
1326
+ })
1327
+ .transacting(trx)
956
1328
 
957
- if (doCommit) {
958
- await trx.commit
959
- }
960
- } catch (err) {
961
- if (doCommit) {
962
- await trx.rollback
963
- }
964
- throw err
1329
+ await knex('transferStateChange')
1330
+ .insert({
1331
+ transferId: payload.transferId,
1332
+ transferStateId: enums.transferState.RECEIVED_PREPARE,
1333
+ reason: payload.reason,
1334
+ createdDate: transactionTimestamp
1335
+ })
1336
+ .transacting(trx)
1337
+
1338
+ // Save transaction reference and transfer extensions
1339
+ let transferExtensions = []
1340
+ transferExtensions.push({
1341
+ transferId: payload.transferId,
1342
+ key: 'externalReference',
1343
+ value: payload.externalReference,
1344
+ createdDate: transactionTimestamp
1345
+ })
1346
+ if (payload.extensionList && payload.extensionList.extension) {
1347
+ transferExtensions = transferExtensions.concat(
1348
+ payload.extensionList.extension.map(ext => {
1349
+ return {
1350
+ transferId: payload.transferId,
1351
+ key: ext.key,
1352
+ value: ext.value,
1353
+ createdDate: transactionTimestamp
1354
+ }
1355
+ })
1356
+ )
1357
+ }
1358
+ for (const transferExtension of transferExtensions) {
1359
+ await knex('transferExtension').insert(transferExtension).transacting(trx)
965
1360
  }
966
1361
  }
967
1362
 
968
1363
  if (trx) {
969
- await trxFunction(trx, false)
1364
+ await trxFunction(trx)
970
1365
  } else {
971
1366
  await knex.transaction(trxFunction)
972
1367
  }
@@ -980,38 +1375,27 @@ const reconciliationTransferReserve = async function (payload, transactionTimest
980
1375
  try {
981
1376
  const knex = await Db.getKnex()
982
1377
 
983
- const trxFunction = async (trx, doCommit = true) => {
984
- try {
985
- const param1 = {
986
- transferId: payload.transferId,
987
- transferStateId: enums.transferState.RESERVED,
988
- reason: payload.reason,
989
- createdDate: transactionTimestamp,
990
- drUpdated: true,
991
- crUpdated: false
992
- }
993
- const positionResult = await TransferFacade.transferStateAndPositionUpdate(param1, enums, trx)
994
-
995
- if (payload.action === Enum.Transfers.AdminTransferAction.RECORD_FUNDS_OUT_PREPARE_RESERVE &&
996
- positionResult.drPositionValue > 0) {
997
- payload.reason = 'Aborted due to insufficient funds'
998
- payload.action = Enum.Transfers.AdminTransferAction.RECORD_FUNDS_OUT_ABORT
999
- await TransferFacade.reconciliationTransferAbort(payload, transactionTimestamp, enums, trx)
1000
- }
1378
+ const trxFunction = async (trx) => {
1379
+ const param1 = {
1380
+ transferId: payload.transferId,
1381
+ transferStateId: enums.transferState.RESERVED,
1382
+ reason: payload.reason,
1383
+ createdDate: transactionTimestamp,
1384
+ drUpdated: true,
1385
+ crUpdated: false
1386
+ }
1387
+ const positionResult = await TransferFacade.transferStateAndPositionUpdate(param1, enums, trx)
1001
1388
 
1002
- if (doCommit) {
1003
- await trx.commit
1004
- }
1005
- } catch (err) {
1006
- if (doCommit) {
1007
- await trx.rollback
1008
- }
1009
- throw err
1389
+ if (payload.action === Enum.Transfers.AdminTransferAction.RECORD_FUNDS_OUT_PREPARE_RESERVE &&
1390
+ positionResult.drPositionValue > 0) {
1391
+ payload.reason = 'Aborted due to insufficient funds'
1392
+ payload.action = Enum.Transfers.AdminTransferAction.RECORD_FUNDS_OUT_ABORT
1393
+ await TransferFacade.reconciliationTransferAbort(payload, transactionTimestamp, enums, trx)
1010
1394
  }
1011
1395
  }
1012
1396
 
1013
1397
  if (trx) {
1014
- await trxFunction(trx, false)
1398
+ await trxFunction(trx)
1015
1399
  } else {
1016
1400
  await knex.transaction(trxFunction)
1017
1401
  }
@@ -1025,55 +1409,44 @@ const reconciliationTransferCommit = async function (payload, transactionTimesta
1025
1409
  try {
1026
1410
  const knex = await Db.getKnex()
1027
1411
 
1028
- const trxFunction = async (trx, doCommit = true) => {
1029
- try {
1030
- // Persist transfer state and participant position change
1031
- const transferId = payload.transferId
1032
- await knex('transferFulfilmentDuplicateCheck')
1033
- .insert({
1034
- transferId
1035
- })
1036
- .transacting(trx)
1037
-
1038
- await knex('transferFulfilment')
1039
- .insert({
1040
- transferId,
1041
- ilpFulfilment: 0,
1042
- completedDate: transactionTimestamp,
1043
- isValid: 1,
1044
- settlementWindowId: null,
1045
- createdDate: transactionTimestamp
1046
- })
1047
- .transacting(trx)
1048
-
1049
- if (payload.action === Enum.Transfers.AdminTransferAction.RECORD_FUNDS_IN ||
1050
- payload.action === Enum.Transfers.AdminTransferAction.RECORD_FUNDS_OUT_COMMIT) {
1051
- const param1 = {
1052
- transferId: payload.transferId,
1053
- transferStateId: enums.transferState.COMMITTED,
1054
- reason: payload.reason,
1055
- createdDate: transactionTimestamp,
1056
- drUpdated: false,
1057
- crUpdated: true
1058
- }
1059
- await TransferFacade.transferStateAndPositionUpdate(param1, enums, trx)
1060
- } else {
1061
- throw new Error('Action not allowed for reconciliationTransferCommit')
1062
- }
1412
+ const trxFunction = async (trx) => {
1413
+ // Persist transfer state and participant position change
1414
+ const transferId = payload.transferId
1415
+ await knex('transferFulfilmentDuplicateCheck')
1416
+ .insert({
1417
+ transferId
1418
+ })
1419
+ .transacting(trx)
1420
+
1421
+ await knex('transferFulfilment')
1422
+ .insert({
1423
+ transferId,
1424
+ ilpFulfilment: 0,
1425
+ completedDate: transactionTimestamp,
1426
+ isValid: 1,
1427
+ settlementWindowId: null,
1428
+ createdDate: transactionTimestamp
1429
+ })
1430
+ .transacting(trx)
1063
1431
 
1064
- if (doCommit) {
1065
- await trx.commit
1066
- }
1067
- } catch (err) {
1068
- if (doCommit) {
1069
- await trx.rollback
1432
+ if (payload.action === Enum.Transfers.AdminTransferAction.RECORD_FUNDS_IN ||
1433
+ payload.action === Enum.Transfers.AdminTransferAction.RECORD_FUNDS_OUT_COMMIT) {
1434
+ const param1 = {
1435
+ transferId: payload.transferId,
1436
+ transferStateId: enums.transferState.COMMITTED,
1437
+ reason: payload.reason,
1438
+ createdDate: transactionTimestamp,
1439
+ drUpdated: false,
1440
+ crUpdated: true
1070
1441
  }
1071
- throw err
1442
+ await TransferFacade.transferStateAndPositionUpdate(param1, enums, trx)
1443
+ } else {
1444
+ throw new Error('Action not allowed for reconciliationTransferCommit')
1072
1445
  }
1073
1446
  }
1074
1447
 
1075
1448
  if (trx) {
1076
- await trxFunction(trx, false)
1449
+ await trxFunction(trx)
1077
1450
  } else {
1078
1451
  await knex.transaction(trxFunction)
1079
1452
  }
@@ -1087,54 +1460,43 @@ const reconciliationTransferAbort = async function (payload, transactionTimestam
1087
1460
  try {
1088
1461
  const knex = await Db.getKnex()
1089
1462
 
1090
- const trxFunction = async (trx, doCommit = true) => {
1091
- try {
1092
- // Persist transfer state and participant position change
1093
- const transferId = payload.transferId
1094
- await knex('transferFulfilmentDuplicateCheck')
1095
- .insert({
1096
- transferId
1097
- })
1098
- .transacting(trx)
1099
-
1100
- await knex('transferFulfilment')
1101
- .insert({
1102
- transferId,
1103
- ilpFulfilment: 0,
1104
- completedDate: transactionTimestamp,
1105
- isValid: 1,
1106
- settlementWindowId: null,
1107
- createdDate: transactionTimestamp
1108
- })
1109
- .transacting(trx)
1110
-
1111
- if (payload.action === Enum.Transfers.AdminTransferAction.RECORD_FUNDS_OUT_ABORT) {
1112
- const param1 = {
1113
- transferId: payload.transferId,
1114
- transferStateId: enums.transferState.ABORTED_REJECTED,
1115
- reason: payload.reason,
1116
- createdDate: transactionTimestamp,
1117
- drUpdated: true,
1118
- crUpdated: false
1119
- }
1120
- await TransferFacade.transferStateAndPositionUpdate(param1, enums, trx)
1121
- } else {
1122
- throw new Error('Action not allowed for reconciliationTransferAbort')
1123
- }
1463
+ const trxFunction = async (trx) => {
1464
+ // Persist transfer state and participant position change
1465
+ const transferId = payload.transferId
1466
+ await knex('transferFulfilmentDuplicateCheck')
1467
+ .insert({
1468
+ transferId
1469
+ })
1470
+ .transacting(trx)
1471
+
1472
+ await knex('transferFulfilment')
1473
+ .insert({
1474
+ transferId,
1475
+ ilpFulfilment: 0,
1476
+ completedDate: transactionTimestamp,
1477
+ isValid: 1,
1478
+ settlementWindowId: null,
1479
+ createdDate: transactionTimestamp
1480
+ })
1481
+ .transacting(trx)
1124
1482
 
1125
- if (doCommit) {
1126
- await trx.commit
1127
- }
1128
- } catch (err) {
1129
- if (doCommit) {
1130
- await trx.rollback
1483
+ if (payload.action === Enum.Transfers.AdminTransferAction.RECORD_FUNDS_OUT_ABORT) {
1484
+ const param1 = {
1485
+ transferId: payload.transferId,
1486
+ transferStateId: enums.transferState.ABORTED_REJECTED,
1487
+ reason: payload.reason,
1488
+ createdDate: transactionTimestamp,
1489
+ drUpdated: true,
1490
+ crUpdated: false
1131
1491
  }
1132
- throw err
1492
+ await TransferFacade.transferStateAndPositionUpdate(param1, enums, trx)
1493
+ } else {
1494
+ throw new Error('Action not allowed for reconciliationTransferAbort')
1133
1495
  }
1134
1496
  }
1135
1497
 
1136
1498
  if (trx) {
1137
- await trxFunction(trx, false)
1499
+ await trxFunction(trx)
1138
1500
  } else {
1139
1501
  await knex.transaction(trxFunction)
1140
1502
  }
@@ -1151,11 +1513,9 @@ const getTransferParticipant = async (participantName, transferId) => {
1151
1513
  .where({
1152
1514
  'participant.name': participantName,
1153
1515
  'tp.transferId': transferId,
1154
- 'participant.isActive': 1,
1155
- 'pc.isActive': 1
1516
+ 'participant.isActive': 1
1156
1517
  })
1157
- .innerJoin('participantCurrency AS pc', 'pc.participantId', 'participant.participantId')
1158
- .innerJoin('transferParticipant AS tp', 'tp.participantCurrencyId', 'pc.participantCurrencyId')
1518
+ .innerJoin('transferParticipant AS tp', 'tp.participantId', 'participant.participantId')
1159
1519
  .select(
1160
1520
  'tp.*'
1161
1521
  )
@@ -1173,10 +1533,8 @@ const recordFundsIn = async (payload, transactionTimestamp, enums) => {
1173
1533
  await TransferFacade.reconciliationTransferPrepare(payload, transactionTimestamp, enums, trx)
1174
1534
  await TransferFacade.reconciliationTransferReserve(payload, transactionTimestamp, enums, trx)
1175
1535
  await TransferFacade.reconciliationTransferCommit(payload, transactionTimestamp, enums, trx)
1176
- await trx.commit
1177
1536
  } catch (err) {
1178
- Logger.isErrorEnabled && Logger.error(err)
1179
- await trx.rollback
1537
+ logger.error('error in recordFundsIn:', err)
1180
1538
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
1181
1539
  }
1182
1540
  })
@@ -1197,7 +1555,8 @@ const TransferFacade = {
1197
1555
  reconciliationTransferCommit,
1198
1556
  reconciliationTransferAbort,
1199
1557
  getTransferParticipant,
1200
- recordFundsIn
1558
+ recordFundsIn,
1559
+ updatePrepareReservedForwarded
1201
1560
  }
1202
1561
 
1203
1562
  module.exports = TransferFacade