Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: #2123 default settlement model #351

Merged
merged 11 commits into from
May 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .ncurc.json

This file was deleted.

10 changes: 1 addition & 9 deletions .ncurc.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
{
"reject":
[
"mustache",
"nodemon",
"npm-check-updates",
"nyc",
"sinon",
"standard",
"tape",
],
[],
}
362 changes: 26 additions & 336 deletions audit-resolve.json

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions docker/central-ledger/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,5 @@
}
}
}
},
"SETTLEMENT_MODELS": [ "DEFERREDNET" ]
}
}
2,306 changes: 764 additions & 1,542 deletions package-lock.json

Large diffs are not rendered by default.

42 changes: 21 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,52 +24,52 @@
},
"dependencies": {
"@hapi/basic": "6.0.0",
"@hapi/boom": "9.1.0",
"@hapi/catbox-memory": "5.0.0",
"@hapi/boom": "9.1.2",
"@hapi/catbox-memory": "5.0.1",
"@hapi/good": "9.0.1",
"@hapi/hapi": "20.1.0",
"@hapi/inert": "6.0.1",
"@hapi/vision": "6.0.0",
"@mojaloop/central-ledger": "13.0.0",
"@mojaloop/central-services-database": "10.6.1",
"@mojaloop/central-services-error-handling": "11.1.0",
"@mojaloop/central-services-health": "11.0.0",
"@mojaloop/central-services-logger": "10.6.0",
"@mojaloop/central-services-shared": "11.6.0",
"@mojaloop/central-services-stream": "10.6.0",
"@mojaloop/ml-number": "11.0.0",
"@now-ims/hapi-now-auth": "2.0.3",
"@hapi/hapi": "20.1.3",
"@hapi/inert": "6.0.3",
"@hapi/vision": "6.0.1",
"@mojaloop/central-ledger": "13.10.0",
"@mojaloop/central-services-database": "10.7.0",
"@mojaloop/central-services-error-handling": "11.3.0",
"@mojaloop/central-services-health": "13.0.0",
"@mojaloop/central-services-logger": "10.6.1",
"@mojaloop/central-services-shared": "13.0.1",
"@mojaloop/central-services-stream": "10.7.0",
"@mojaloop/ml-number": "11.1.0",
"@now-ims/hapi-now-auth": "2.0.4",
"async": "3.2.0",
"async-retry": "1.3.1",
"bignumber.js": "9.0.1",
"blipp": "4.0.2",
"hapi-auth-bearer-token": "8.0.0",
"hapi-openapi": "2.0.2",
"hapi-swagger": "14.1.0",
"hapi-openapi": "3.0.0",
"hapi-swagger": "14.1.3",
"lodash": "4.17.21",
"mustache": "4.1.0",
"mustache": "4.2.0",
"parse-strings-in-object": "2.0.0",
"rc": "1.2.8",
"uuid4": "2.0.2",
"vm": "0.1.0"
},
"devDependencies": {
"@hapi/joi": "17.1.1",
"@types/lodash": "4.14.168",
"@types/lodash": "4.14.169",
"axios": "0.21.1",
"bluebird": "3.7.2",
"chai": "4.3.4",
"chai-exclude": "2.0.3",
"chai-subset": "1.6.0",
"eslint": "7.23.0",
"eslint": "7.26.0",
"faucet": "0.0.1",
"get-port": "5.1.1",
"jest": "26.6.3",
"jest-junit": "12.0.0",
"node-fetch": "2.6.1",
"nodemon": "2.0.7",
"npm-audit-resolver": "2.2.1",
"npm-check-updates": "11.4.1",
"npm-audit-resolver": "2.3.0",
"npm-check-updates": "11.5.12",
"nyc": "15.1.0",
"pre-commit": "1.2.2",
"proxyquire": "2.1.3",
Expand Down
4 changes: 4 additions & 0 deletions scripts/transferSettlementTemp/interchangeFeeCalculation.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ const payerFspId = transfer.payer.partyIdInfo.fspId
const payeeFspId = transfer.payee.partyIdInfo.fspId

if ((payeeFspId !== payerFspId) &&
(transfer.payee.partyIdInfo.extensionList && // WORKAROUND for issue #2149
transfer.payer.partyIdInfo.extensionList && // WORKAROUND for issue #2149
transfer.payee.partyIdInfo.extensionList.extension && // WORKAROUND for issue #2149
transfer.payer.partyIdInfo.extensionList.extension) && // WORKAROUND for issue #2149
(getExtensionValue(transfer.payee.partyIdInfo.extensionList.extension, 'accountType') === 'Wallet' &&
getExtensionValue(transfer.payer.partyIdInfo.extensionList.extension, 'accountType') === 'Wallet') &&
(transfer.transactionType.scenario === 'TRANSFER' &&
Expand Down
13 changes: 10 additions & 3 deletions src/models/settlement/facade.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const Config = require('../../lib/config')
const ParticipantFacade = require('@mojaloop/central-ledger/src/models/participant/facade')
const Enums = require('../lib/enums')
const Utility = require('../../lib/utility')
const SettlementModelModel = require('./settlementModel')

const groupByWindowsWithContent = (records) => {
const settlementWindowsAssoc = {}
Expand Down Expand Up @@ -1392,7 +1393,7 @@ const Facade = {
await knex.batchInsert('settlementSettlementWindow', settlementSettlementWindowList).transacting(trx)

// retrieve affected settlementWindowContent
const swcIdList = await knex('settlementWindow AS sw').transacting(trx)
let swcList = await knex('settlementWindow AS sw').transacting(trx)
.join('settlementWindowStateChange AS swsc', 'swsc.settlementWindowStateChangeId', 'sw.currentStateChangeId')
.join('settlementWindowContent AS swc', 'swc.settlementWindowId', 'sw.settlementWindowId')
.join('settlementWindowContentStateChange AS swcsc', 'swcsc.settlementWindowContentStateChangeId', 'swc.currentStateChangeId')
Expand All @@ -1401,8 +1402,14 @@ const Facade = {
.where('swc.currencyId', knex.raw('COALESCE(?, swc.currencyId)', settlementModel.currencyId))
.whereIn('swsc.settlementWindowStateId', [enums.settlementWindowStates.CLOSED, enums.settlementWindowStates.ABORTED, enums.settlementWindowStates.PENDING_SETTLEMENT])
.whereIn('swcsc.settlementWindowStateId', [enums.settlementWindowStates.CLOSED, enums.settlementWindowStates.ABORTED])
.distinct('swc.settlementWindowContentId')
const swcIdArray = swcIdList.map(record => record.settlementWindowContentId)

if (settlementModel.currencyId === null) { // Default settlement model
const allSettlementModels = await SettlementModelModel.getAll()
const settlementModelCurrenciesList = allSettlementModels.filter(record => record.currencyId !== null).map(record => record.currencyId)
swcList = swcList.filter(swc => !settlementModelCurrenciesList.includes(swc.currencyId))
}

const swcIdArray = swcList.map(record => record.settlementWindowContentId)

// bind requested settlementWindowContent and settlementContentAggregation records
await knex('settlementWindowContent').transacting(trx)
Expand Down
7 changes: 6 additions & 1 deletion src/models/settlement/settlementModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ const getByName = async (name) => {
return Db.from('settlementModel').findOne({ name, isActive: 1 })
}

const getAll = async () => {
return await Db.from('settlementModel').find({ isActive: 1 })
}

module.exports = {
getByName
getByName,
getAll
}
9 changes: 8 additions & 1 deletion src/models/transferSettlement/facade.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ async function updateTransferSettlement (transferId, status, trx = null) {
async function getSettlementModelByTransferId (transferId, settlementGranularityName) {
Logger.info(Utility.breadcrumb(location, { method: 'getSettlementModelByTransferId' }))
const knex = await Db.getKnex()
return knex('settlementModel')
const settlementModelByTransferId = await knex('settlementModel')
.join('participantCurrency AS pc', function () {
this.on('pc.currencyId', 'settlementModel.currencyId')
.andOn('pc.ledgerAccountTypeId', 'settlementModel.ledgerAccountTypeId')
Expand All @@ -318,7 +318,14 @@ async function getSettlementModelByTransferId (transferId, settlementGranularity
.where('g.name', settlementGranularityName)
.where('settlementModel.isActive', 1)
.select('settlementModel.*')
if (settlementModelByTransferId.length === 0) {
const allSettlementModels = await Db.from('settlementModel').find()
const defaultGrossSettlementModel = allSettlementModels.filter(sm => (sm.currencyId === null && sm.settlementGranularityId === SettlementEnum.SettlementGranularity.GROSS))
return defaultGrossSettlementModel
}
vgenev marked this conversation as resolved.
Show resolved Hide resolved
return settlementModelByTransferId
}

const Facade = {
insertLedgerEntry,
insertLedgerEntries,
Expand Down
3 changes: 1 addition & 2 deletions test/integration-config-centralledger.json
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,5 @@
}
}
}
},
"SETTLEMENT_MODELS": ["DEFERREDNET"]
}
}
2 changes: 1 addition & 1 deletion test/integration-runner.sh
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ start_db() {
-e MYSQL_PASSWORD=$DB_PASSWORD \
-e MYSQL_DATABASE=$DB_NAME \
-e MYSQL_ALLOW_EMPTY_PASSWORD=true \
$DB_IMAGE:$DB_TAG
$DB_IMAGE:$DB_TAG --log-error=stdout
}

fdb() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const TransferModel = require('@mojaloop/central-ledger/src/models/transfer/tran

// require('leaked-handles').set({ fullStack: true, timeout: 5000, debugSockets: true })

const currency = 'USD'
const currency = 'EUR' // FOR THE SAKE OF PARTICIPANT FILTER ONLY TO BE ABLE TO RUN OUR TESTS
let netSettlementSenderId
let netSenderAccountId
let netSettlementRecipientId
Expand All @@ -75,6 +75,14 @@ const settlementModels = [
ledgerAccountTypeId: 1, // POSITION
autoPositionReset: true,
currencyId: 'USD'
},
{
name: 'DEFAULTDEFERREDNET',
settlementGranularityId: 2, // NET
settlementInterchangeId: 2, // MULTILATERAL
settlementDelayId: 2, // DEFERRED
ledgerAccountTypeId: 1, // POSITION
autoPositionReset: true
}
]

Expand Down Expand Up @@ -154,10 +162,10 @@ Test('SettlementTransfer should', async settlementTransferTest => {
}
})

await settlementTransferTest.test('create a settlement:', async test => {
await settlementTransferTest.test(`create a settlement using the ${settlementModels[2].name} Settlement Model:`, async test => {
try {
const params = {
settlementModel: settlementModels[1].name,
settlementModel: settlementModels[2].name,
reason: 'reason',
settlementWindows: [
{
Expand Down Expand Up @@ -430,7 +438,7 @@ Test('SettlementTransfer should', async settlementTransferTest => {
test.equal(payeeTransferStateChangeRecord.transferStateId, enums.transferStates.COMMITTED, '#48 settlement transfer for payee is COMMITTED')

const currentPayerPosition = (await ParticipantPositionModel.getPositionByCurrencyId(netSenderAccountId)).value
test.equal(currentPayerPosition, initialPayerPosition - netSettlementAmount, '#49 position for NET_SETTLEMENT_SENDER is adjusted')
test.equal(currentPayerPosition, new MLNumber(initialPayerPosition).subtract(netSettlementAmount).toNumber(), '#49 position for NET_SETTLEMENT_SENDER is adjusted')

const currentPayeePosition = (await ParticipantPositionModel.getPositionByCurrencyId(netRecipientAccountId)).value
test.equal(currentPayeePosition, initialPayeePosition, '#50 position for NET_SETTLEMENT_RECIPIENT is unchanged')
Expand Down Expand Up @@ -554,7 +562,7 @@ Test('SettlementTransfer should', async settlementTransferTest => {
}
})

await settlementTransferTest.test('#62 create a settlement with previous window remaining content and settle it:', async test => {
await settlementTransferTest.test(`#62 create a settlement using the ${settlementModels[0].name} Settlement Model and settle it:`, async test => {
try {
const settlementStates = [enums.settlementStates.PS_TRANSFERS_RECORDED, enums.settlementStates.PS_TRANSFERS_RESERVED, enums.settlementStates.PS_TRANSFERS_COMMITTED, enums.settlementStates.SETTLED]

Expand Down Expand Up @@ -606,8 +614,68 @@ Test('SettlementTransfer should', async settlementTransferTest => {
const windowContentState = await Models.settlementWindowContentStateChange.getBySettlementWindowContentId(res.settlementWindows[0].content[0].id)
test.equal(windowContentState.settlementWindowStateId, 'SETTLED', '#65 settlement window content state is SETTLED')

test.end()
} catch (err) {
Logger.error(`settlementTransferTest failed with error - ${err}`)
test.fail()
test.end()
}
})

await settlementTransferTest.test(`#66 create a settlement using the ${settlementModels[1].name} Settlement Model and settle it:`, async test => {
try {
const settlementStates = [enums.settlementStates.PS_TRANSFERS_RECORDED, enums.settlementStates.PS_TRANSFERS_RESERVED, enums.settlementStates.PS_TRANSFERS_COMMITTED, enums.settlementStates.SETTLED]

let params = {
settlementModel: settlementModels[1].name,
reason: 'reason',
settlementWindows: [
{
id: settlementWindowId
}
]
}
settlementData = await SettlementService.settlementEventTrigger(params, enums)
test.ok(settlementData, '#67 settlementEventTrigger operation success')

let res
for (const state of settlementStates) {
params = {
participants: [
{
id: settlementData.participants[0].id,
accounts: [
{
id: settlementData.participants[0].accounts[0].id,
reason: `Settlement to ${state} state`,
state
}
]
},
{
id: settlementData.participants[1].id,
accounts: [
{
id: settlementData.participants[1].accounts[0].id,
reason: `Settlement to ${state} state`,
state
}
]
}
]
}
res = await SettlementService.putById(settlementData.id, params, enums)
test.ok(res, `settlement putById operation success for ${state} state`)
}

const settlementState = await SettlementStateChangeModel.getBySettlementId(settlementData.id)
test.equal(settlementState.settlementStateId, enums.settlementStates.SETTLED, '#68 settlement state is SETTLED')

const windowContentState = await Models.settlementWindowContentStateChange.getBySettlementWindowContentId(res.settlementWindows[0].content[0].id)
test.equal(windowContentState.settlementWindowStateId, 'SETTLED', '#69 settlement window content state is SETTLED')

const window = await SettlementWindowStateChangeModel.getBySettlementWindowId(res.settlementWindows[0].id)
test.equal(window.settlementWindowStateId, enums.settlementWindowStates.SETTLED, '#66 window is SETTLED because there is no more window content to settle')
test.equal(window.settlementWindowStateId, enums.settlementWindowStates.SETTLED, '#70 window is SETTLED because there is no more window content to settle')

test.end()
} catch (err) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const Api = require('../helpers/api')
const Db = require('../../../src/lib/db')
const Utils = require('../helpers/utils')

const currencies = ['USD', 'TZS']
const currencies = ['USD', 'TZS', 'EUR']

const settlementModels = [
{
Expand All @@ -58,6 +58,16 @@ const settlementModels = [
autoPositionReset: true,
currency: 'USD',
requireLiquidityCheck: true
},
{
name: 'DEFAULTDEFERREDNET',
settlementGranularity: 'NET',
settlementInterchange: 'MULTILATERAL',
settlementAccountType: 'SETTLEMENT',
settlementDelay: 'DEFERRED',
ledgerAccountType: 'POSITION',
autoPositionReset: true,
requireLiquidityCheck: true
}
]

Expand Down Expand Up @@ -125,6 +135,7 @@ async function initSettlementModels () {
await knex.raw('SET FOREIGN_KEY_CHECKS = 1;')
await Api.createSettlementModel(settlementModels[0])
await Api.createSettlementModel(settlementModels[1])
await Api.createSettlementModel(settlementModels[2])
}

/**
Expand Down
4 changes: 1 addition & 3 deletions test/unit/models/settlement/facade.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4312,9 +4312,7 @@ Test('Settlement facade', async (settlementFacadeTest) => {
where: sandbox.stub().returns({
where: sandbox.stub().returns({
whereIn: sandbox.stub().returns({
whereIn: sandbox.stub().returns({
distinct: sandbox.stub().returns(stubData.triggerSettlementEvent.swcIdList)
})
whereIn: sandbox.stub().returns(stubData.triggerSettlementEvent.swcIdList)
})
})
})
Expand Down
Loading