@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.
- package/.ncurc.yaml +3 -1
- package/.nvmrc +1 -1
- package/.nycrc.yml +16 -1
- package/Dockerfile +9 -10
- package/README.md +39 -9
- package/audit-ci.jsonc +15 -2
- package/config/default.json +17 -1
- package/docker/central-ledger/default.json +9 -0
- package/docker/config-modifier/configs/central-ledger.js +25 -0
- package/docker/env.sh +15 -0
- package/docker/kafka/scripts/provision.sh +4 -1
- package/docker/ml-api-adapter/default.json +4 -1
- package/docker-compose.yml +90 -2
- package/documentation/db/erd-transfer-timeout.png +0 -0
- package/documentation/db/erd-transfer-timeout.txt +81 -0
- package/documentation/fx-implementation/README.md +48 -0
- package/documentation/fx-implementation/assets/fx-position-movements.drawio.svg +4 -0
- package/documentation/fx-implementation/assets/test-scenario.drawio.svg +4 -0
- package/documentation/sequence-diagrams/Handler - FX timeout.plantuml +123 -0
- package/documentation/sequence-diagrams/Handler - FX timeout.png +0 -0
- package/documentation/sequence-diagrams/Handler - timeout.plantuml +81 -0
- package/documentation/sequence-diagrams/Handler - timeout.png +0 -0
- package/documentation/state-diagrams/transfer-ML-spec-states-diagram.png +0 -0
- package/documentation/state-diagrams/transfer-internal-states-diagram.png +0 -0
- package/documentation/state-diagrams/transfer-internal-states.plantuml +75 -0
- package/documentation/state-diagrams/transfer-states.plantuml +13 -0
- package/migrations/310204_transferParticipant-participantId.js +52 -0
- package/migrations/310403_participantPositionChange-participantCurrencyId.js +47 -0
- package/migrations/310404_participantPositionChange-change.js +46 -0
- package/migrations/600010_fxTransferType.js +43 -0
- package/migrations/600011_fxTransferType-indexes.js +38 -0
- package/migrations/600012_fxParticipantCurrencyType.js +43 -0
- package/migrations/600013_fxParticipantCurrencyType-indexes.js +38 -0
- package/migrations/600100_fxTransferDuplicateCheck.js +42 -0
- package/migrations/600110_fxTransferErrorDuplicateCheck.js.js +17 -0
- package/migrations/600200_fxTransfer.js +51 -0
- package/migrations/600201_fxTransfer-indexes.js +40 -0
- package/migrations/600400_fxTransferStateChange.js +46 -0
- package/migrations/600401_fxTransferStateChange-indexes.js +40 -0
- package/migrations/600501_fxWatchList.js +46 -0
- package/migrations/600502_fxWatchList-indexes.js +40 -0
- package/migrations/600600_fxTransferFulfilmentDuplicateCheck.js +43 -0
- package/migrations/600601_fxTransferFulfilmentDuplicateCheck-indexes.js +38 -0
- package/migrations/600700_fxTransferFulfilment.js +47 -0
- package/migrations/600701_fxTransferFulfilment-indexes.js +43 -0
- package/migrations/600800_fxTransferExtension.js +47 -0
- package/migrations/601400_fxTransferTimeout.js +43 -0
- package/migrations/601401_fxTransferTimeout-indexes.js +37 -0
- package/migrations/601500_fxTransferError.js +44 -0
- package/migrations/601501_fxTransferError-indexes.js +37 -0
- package/migrations/610200_fxTransferParticipant.js +52 -0
- package/migrations/610201_fxTransferParticipant-indexes.js +44 -0
- package/migrations/610202_fxTransferParticipant-participantId.js +52 -0
- package/migrations/610403_participantPositionChange-fxTransfer.js +46 -0
- package/migrations/910101_feature904DataMigration.js +46 -52
- package/migrations/910102_feature949DataMigration.js +219 -225
- package/migrations/950104_settlementModel-settlementAccountTypeId.js +15 -20
- package/migrations/950108_participantProxy.js +18 -0
- package/migrations/950109_fxQuote.js +19 -0
- package/migrations/950110_fxQuoteResponse.js +25 -0
- package/migrations/950111_fxQuoteError.js +23 -0
- package/migrations/950113_fxQuoteDuplicateCheck.js +18 -0
- package/migrations/950114_fxQuoteResponseDuplicateCheck.js +21 -0
- package/migrations/950115_fxQuoteConversionTerms.js +36 -0
- package/migrations/950116_fxQuoteConversionTermsExtension.js +21 -0
- package/migrations/950117_fxQuoteResponseConversionTerms.js +39 -0
- package/migrations/950118_fxQuoteResponseConversionTermsExtension.js +21 -0
- package/migrations/950119_fxCharge.js +27 -0
- package/migrations/960100_create_externalParticipant.js +47 -0
- package/migrations/960110_alter_transferParticipant__addFiled_externalParticipantId.js +50 -0
- package/migrations/960111_alter_fxTransferParticipant__addFiled_externalParticipantId.js +50 -0
- package/package.json +27 -20
- package/seeds/endpointType.js +18 -0
- package/seeds/fxParticipantCurrencyType.js +45 -0
- package/seeds/fxTransferType.js +45 -0
- package/seeds/participant.js +2 -1
- package/seeds/transferParticipantRoleType.js +9 -0
- package/seeds/transferState.js +10 -0
- package/src/api/interface/swagger.json +23 -30
- package/src/api/participants/handler.js +6 -2
- package/src/api/participants/routes.js +8 -7
- package/src/api/root/handler.js +15 -5
- package/src/domain/fx/cyril.js +466 -0
- package/src/domain/fx/index.js +107 -0
- package/src/domain/participant/index.js +108 -1
- package/src/domain/position/abort.js +215 -0
- package/src/domain/position/binProcessor.js +361 -99
- package/src/domain/position/fulfil.js +252 -111
- package/src/domain/position/fx-fulfil.js +138 -0
- package/src/domain/position/fx-prepare.js +280 -0
- package/src/domain/position/fx-timeout-reserved.js +159 -0
- package/src/domain/position/index.js +1 -0
- package/src/domain/position/prepare.js +69 -49
- package/src/domain/position/timeout-reserved.js +162 -0
- package/src/domain/timeout/index.js +26 -2
- package/src/domain/transfer/index.js +22 -5
- package/src/domain/transfer/transform.js +19 -6
- package/src/handlers/admin/handler.js +0 -2
- package/src/handlers/bulk/fulfil/handler.js +5 -5
- package/src/handlers/bulk/get/handler.js +5 -5
- package/src/handlers/bulk/prepare/handler.js +9 -9
- package/src/handlers/bulk/processing/handler.js +8 -7
- package/src/handlers/bulk/shared/validator.js +1 -1
- package/src/handlers/positions/handler.js +23 -10
- package/src/handlers/positions/handlerBatch.js +54 -26
- package/src/handlers/register.js +2 -1
- package/src/handlers/timeouts/handler.js +212 -68
- package/src/handlers/transfers/FxFulfilService.js +387 -0
- package/src/handlers/transfers/createRemittanceEntity.js +106 -0
- package/src/handlers/transfers/dto.js +53 -0
- package/src/handlers/transfers/handler.js +609 -571
- package/src/handlers/transfers/prepare.js +572 -0
- package/src/handlers/transfers/validator.js +75 -14
- package/src/lib/cache.js +1 -1
- package/src/lib/config.js +6 -3
- package/src/lib/healthCheck/subServiceHealth.js +12 -2
- package/src/lib/proxyCache.js +131 -0
- package/src/models/bulkTransfer/facade.js +58 -82
- package/src/models/fxTransfer/duplicateCheck.js +153 -0
- package/src/models/fxTransfer/fxTransfer.js +578 -0
- package/src/models/fxTransfer/fxTransferError.js +53 -0
- package/src/models/fxTransfer/fxTransferExtension.js +41 -0
- package/src/models/fxTransfer/fxTransferTimeout.js +68 -0
- package/src/models/fxTransfer/index.js +15 -0
- package/src/models/fxTransfer/stateChange.js +47 -0
- package/src/models/fxTransfer/watchList.js +49 -0
- package/src/models/ledgerAccountType/ledgerAccountType.js +8 -32
- package/src/models/misc/segment.js +0 -1
- package/src/models/participant/externalParticipant.js +96 -0
- package/src/models/participant/externalParticipantCached.js +148 -0
- package/src/models/participant/facade.js +192 -123
- package/src/models/participant/participant.js +2 -1
- package/src/models/participant/participantCurrency.js +1 -1
- package/src/models/participant/participantPosition.js +2 -8
- package/src/models/position/batch.js +90 -1
- package/src/models/position/facade.js +5 -5
- package/src/models/position/participantPositionChanges.js +68 -0
- package/src/models/settlement/settlementModel.js +5 -23
- package/src/models/transfer/facade.js +806 -447
- package/src/shared/constants.js +52 -0
- package/src/shared/fspiopErrorFactory.js +131 -0
- package/src/shared/logger/index.js +8 -0
- package/src/shared/loggingPlugin.js +43 -0
- package/src/shared/plugins.js +6 -0
- package/src/shared/setup.js +10 -0
- package/test-integration.Dockerfile +1 -1
- 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
|
|
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
|
-
|
|
41
|
+
|
|
42
|
+
const { logger } = require('../../shared/logger')
|
|
43
|
+
const Db = require('../../lib/db')
|
|
43
44
|
const Config = require('../../lib/config')
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
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('
|
|
68
|
-
.
|
|
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('
|
|
73
|
-
.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
385
|
+
logger.debug('savePayeeTransferResponse::transferExtensionRecordsList')
|
|
371
386
|
}
|
|
372
387
|
await knex('transferStateChange').transacting(trx).insert(transferStateChangeRecord)
|
|
373
388
|
result.transferStateChangeRecord = transferStateChangeRecord
|
|
374
|
-
|
|
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
|
-
|
|
398
|
+
logger.debug('savePayeeTransferResponse::transferError')
|
|
384
399
|
}
|
|
385
400
|
histTPayeeResponseValidationPassedEnd({ success: true, queryName: 'facade_saveTransferPrepared_transaction' })
|
|
386
401
|
result.savePayeeTransferResponseExecuted = true
|
|
387
|
-
|
|
402
|
+
logger.debug('savePayeeTransferResponse::success')
|
|
388
403
|
} catch (err) {
|
|
389
|
-
|
|
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
|
-
|
|
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
|
-
|
|
435
|
+
const participants = {
|
|
436
|
+
[payload.payeeFsp]: {},
|
|
437
|
+
[payload.payerFsp]: {}
|
|
438
|
+
}
|
|
412
439
|
|
|
413
|
-
|
|
414
|
-
|
|
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.
|
|
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
|
-
|
|
421
|
-
|
|
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:
|
|
489
|
+
transferStateId: hasPassedValidation ? TransferInternalState.RECEIVED_PREPARE : TransferInternalState.INVALID,
|
|
441
490
|
reason: stateReason,
|
|
442
491
|
createdDate: Time.getUTCString(new Date())
|
|
443
492
|
}
|
|
444
493
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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')
|
|
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
|
-
})
|
|
618
|
-
// console.log('SQL: ' + q1)
|
|
957
|
+
})
|
|
619
958
|
|
|
620
|
-
// Insert `
|
|
621
|
-
await knex.from(knex.raw('
|
|
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('
|
|
624
|
-
.innerJoin(knex('
|
|
625
|
-
.select('
|
|
626
|
-
.max('
|
|
627
|
-
.
|
|
628
|
-
.
|
|
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('
|
|
631
|
-
.
|
|
632
|
-
.
|
|
633
|
-
.
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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('
|
|
641
|
-
.innerJoin(
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
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
|
-
.
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
// Insert `
|
|
655
|
-
await knex.from(knex.raw('
|
|
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('
|
|
658
|
-
.innerJoin(
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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(
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
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
|
-
|
|
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
|
-
|
|
692
|
-
|
|
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
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
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
|
|
1118
|
+
const trxFunction = async (trx) => {
|
|
731
1119
|
const transactionTimestamp = Time.getUTCString(new Date())
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
.
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
.
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
-
|
|
836
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
866
|
-
|
|
867
|
-
|
|
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
|
-
|
|
884
|
-
|
|
885
|
-
|
|
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
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
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
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
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
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
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
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1305
|
+
// Insert transferParticipant records
|
|
1306
|
+
await knex('transferParticipant')
|
|
1307
|
+
.insert({
|
|
936
1308
|
transferId: payload.transferId,
|
|
937
|
-
|
|
938
|
-
|
|
1309
|
+
participantId: Config.HUB_ID,
|
|
1310
|
+
participantCurrencyId: reconciliationAccountId,
|
|
1311
|
+
transferParticipantRoleTypeId: enums.transferParticipantRoleType.HUB,
|
|
1312
|
+
ledgerEntryTypeId,
|
|
1313
|
+
amount,
|
|
939
1314
|
createdDate: transactionTimestamp
|
|
940
1315
|
})
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
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
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
}
|
|
964
|
-
|
|
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
|
|
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
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
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
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
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
|
|
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
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
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
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
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
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
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
|
-
|
|
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
|
|
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('
|
|
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
|
-
|
|
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
|