diff --git a/src/domain/settlement/index.js b/src/domain/settlement/index.js index bcfaa4a9..41d325f1 100644 --- a/src/domain/settlement/index.js +++ b/src/domain/settlement/index.js @@ -22,6 +22,7 @@ * Gates Foundation - Name Surname + * Georgi Georgiev * Valentin Genev * Deon Botha * Rajiv Mothilal @@ -111,27 +112,29 @@ module.exports = { let settlementAccountList = await settlementsModel.settlementParticipantCurrency.getParticipantCurrencyBySettlementId({settlementId}, enums) let windowsList = await settlementWindowModel.getBySettlementId({settlementId}, enums) let windowsAccountsList = await settlementsModel.getSettlementTransferParticipantBySettlementId({settlementId}, enums) + + // seq-settlement-6.2.5, step 19 let pendingSettlementCount = 0 let settledCount = 0 let notSettledCount = 0 - // let unknownCount = 0 + let unknownCount = 0 let allAccounts = new Map() let allWindows = new Map() let windowsAccounts = new Map() let accountsWindows = new Map() - for (let data of settlementAccountList) { - allAccounts[data.participantCurrencyId] = { - id: data.participantCurrencyId, - state: data.state, - reason: data.reason, - createDate: data.createdDate, + for (let account of settlementAccountList) { + allAccounts[account.participantCurrencyId] = { + id: account.participantCurrencyId, + state: account.state, + reason: account.reason, + createDate: account.createdDate, netSettlementAmount: { - amount: data.netAmount, - currency: data.currency + amount: account.netAmount, + currency: account.currency }, - key: data.key + key: account.key } - switch (data.state) { + switch (account.state) { case 'PENDING_SETTLEMENT': { pendingSettlementCount++ break @@ -145,25 +148,30 @@ module.exports = { break } default: { - // unknownCount++ + unknownCount++ break } } } let settlementAccounts = { - pendingSettlementCount: pendingSettlementCount, - settledCount: settledCount, - notSettledCount: notSettledCount + pendingSettlementCount, + settledCount, + notSettledCount, + unknownCount } - // let settlementAccountsInit = Object.assign({}, settlementAccounts) + let settlementAccountsInit = Object.assign({}, settlementAccounts) + + // seq-settlement-6.2.5, step 23 for (let window of windowsList) { - allWindows[window.settlementWindowId] = { - id: window.settlementWindowId, + allWindows[window.id] = { + id: window.id, state: window.state, reason: window.reason, createDate: window.createdDate } } + + // seq-settlement-6.2.5, step 24 for (let record of windowsAccountsList) { let wid = record.settlementWindowId let aid = record.participantCurrencyId @@ -197,18 +205,21 @@ module.exports = { } } } - // let windowsAccountsInit = Object.assign({}, windowsAccounts) + let windowsAccountsInit = JSON.parse(JSON.stringify(windowsAccounts)) // TODO: switch to lodash cloneDeep let participants = [] let settlementParticipantCurrencyStateChange = [] let processedAccounts = [] let affectedWindows = [] let transactionTimestamp = new Date() - for (let participant of payload.participants) { + + // seq-settlement-6.2.5, step 26 + for (let participant in payload.participants) { let participantPayload = payload.participants[participant] participants.push({id: participantPayload.id, accounts: []}) let pi = participants.length - 1 participant = participants[pi] - for (let account of participant.accounts) { + // seq-settlement-6.2.5, step 27 + for (let account in participantPayload.accounts) { let accountPayload = participantPayload.accounts[account] if (allAccounts[accountPayload.id] === undefined) { participant.accounts.push({ @@ -230,7 +241,7 @@ module.exports = { errorDescription: 'Account already processed once' } }) - } else if (allAccounts[account.id].state === accountPayload.state) { + } else if (allAccounts[accountPayload.id].state === accountPayload.state) { processedAccounts.push(accountPayload.id) participant.accounts.push({ id: accountPayload.id, @@ -245,8 +256,8 @@ module.exports = { reason: accountPayload.reason }) allAccounts[accountPayload.id].reason = accountPayload.reason - allAccounts[accountPayload.id].createdDate = new Date() - } else if (allAccounts[account.id].state === 'PENDING_SETTLEMENT' && accountPayload.state === 'SETTLED') { + allAccounts[accountPayload.id].createdDate = transactionTimestamp + } else if (allAccounts[accountPayload.id].state === 'PENDING_SETTLEMENT' && accountPayload.state === 'SETTLED') { processedAccounts.push(accountPayload.id) participant.accounts.push({ id: accountPayload.id, @@ -264,9 +275,9 @@ module.exports = { settlementAccounts.settledCount++ allAccounts[accountPayload.id].state = accountPayload.state allAccounts[accountPayload.id].reason = accountPayload.reason - allAccounts[accountPayload.id].createdDate = new Date() + allAccounts[accountPayload.id].createdDate = transactionTimestamp let settlementWindowId - for (let aw of accountsWindows[accountPayload.id].windows) { + for (let aw in accountsWindows[accountPayload.id].windows) { settlementWindowId = accountsWindows[accountPayload.id].windows[aw] windowsAccounts[settlementWindowId].pendingSettlementCount-- windowsAccounts[settlementWindowId].settledCount++ @@ -289,9 +300,27 @@ module.exports = { } } } - settlementId = await settlementsModel.putById(settlementParticipantCurrencyStateChange, payload, enums) - // TODO the transaction insert for everything - return true + + let inputObj = { + settlementParticipantCurrencyStateChange, + affectedWindows, + windowsAccountsInit, + windowsAccounts, + allWindows, + settlementAccounts, + settlementAccountsInit, + settlementData, + transactionTimestamp + } + // seq-settlement-6.2.5, steps 33-47 + let result = await settlementsModel.putById(inputObj, enums) + return { + id: settlementId, + state: result.settlementData.state, + createdDate: result.settlementData.createdDate, + settlementWindows: result.settlementWindows, + participants + } } else { let err = new Error('settlement window not found') Logger('error', err) diff --git a/src/handlers/settlements.js b/src/handlers/settlements.js index 8f305506..6dc28b84 100644 --- a/src/handlers/settlements.js +++ b/src/handlers/settlements.js @@ -22,6 +22,7 @@ * Gates Foundation - Name Surname + * Georgi Georgiev * Valentin Genev * Deon Botha * Rajiv Mothilal @@ -51,7 +52,6 @@ module.exports = { * responses: 200, 400, 401, 404, 415, default */ get: async function getSettlementsByParams (request, h) { - // TODO Logger.info('Here') try { const Enums = await request.server.methods.enums('settlementStates') diff --git a/src/handlers/settlements/{id}.js b/src/handlers/settlements/{id}.js index 26b08f51..a3eface6 100644 --- a/src/handlers/settlements/{id}.js +++ b/src/handlers/settlements/{id}.js @@ -22,6 +22,7 @@ * Gates Foundation - Name Surname + * Georgi Georgiev * Valentin Genev * Deon Botha * Rajiv Mothilal @@ -73,7 +74,6 @@ module.exports = { */ put: async function updateSettlementById (request, h) { - // TODO const settlementId = request.params.id const Enums = await request.server.methods.enums('settlementStates') try { diff --git a/src/interface/swagger.json b/src/interface/swagger.json index 48c8c533..9e8426b8 100644 --- a/src/interface/swagger.json +++ b/src/interface/swagger.json @@ -161,10 +161,13 @@ "name": "state", "type": "string", "enum": [ - "open", - "closed" + "OPEN", + "CLOSED", + "PENDING_SETTLEMENT", + "SETTLED", + "NOT_SETTLED" ], - "description": "A settlement window state (open or closed) to filter on. \n", + "description": "A settlement window state to filter on.\n", "required": false }, { diff --git a/src/models/settlement/facade.js b/src/models/settlement/facade.js index adf5409e..278b7daa 100644 --- a/src/models/settlement/facade.js +++ b/src/models/settlement/facade.js @@ -18,6 +18,7 @@ * Gates Foundation - Name Surname + * Georgi Georgiev * Valentin Genev * Deon Botha -------------- @@ -29,23 +30,114 @@ const Db = require('../index') module.exports = {} -const settlementModel = require('./settlement') +// const settlementModel = require('./settlement') const Facade = { - putById: async function (settlementParticipantCurrencyStateChange, enums = {}) { + putById: async function (obj, enums = {}) { try { + let insertPromises + let updatePromises const knex = await Db.getKnex() - // Open transaction return await knex.transaction(async (trx) => { - const settlementParticipantCurrencyStateChangeIdList = await knex.batchInsert('settlementParticipantCurrencyStateChange', settlementParticipantCurrencyStateChange).transacting(trx) - if (settlementParticipantCurrencyStateChangeIdList) { - for (let id of settlementParticipantCurrencyStateChangeIdList) { - let temp = settlementParticipantCurrencyStateChange[id] - temp.settlementParticipantCurrencyStateChangeId = id + try { + // seq-settlement-6.2.5, step 33 + insertPromises = [] + for (let cpcsc of obj.settlementParticipantCurrencyStateChange) { + // Switched to insert from batchInsert because only last id is returned + insertPromises.push( + knex('settlementParticipantCurrencyStateChange') + .insert(cpcsc).returning('settlementParticipantCurrencyStateChangeId') + .transacting(trx) + ) + } + let settlementParticipantCurrencyStateChangeIdList = (await Promise.all(insertPromises)).map(v => v[0]) + if (settlementParticipantCurrencyStateChangeIdList) { + updatePromises = [] + for (let i in settlementParticipantCurrencyStateChangeIdList) { + updatePromises.push( + knex('settlementParticipantCurrency') + .where('settlementParticipantCurrencyId', obj.settlementParticipantCurrencyStateChange[i].settlementParticipantCurrencyId) + .update({currentStateChangeId: settlementParticipantCurrencyStateChangeIdList[i]}) + .transacting(trx) + ) + } + await Promise.all(updatePromises) } - } else { - throw new Error('insert failed') + + // seq-settlement-6.2.5, step 38 + let settlementWindowStateChange = [] + obj.settlementWindows = [] // response object + let windowAccountsInit + let windowAccounts + for (let aw in obj.affectedWindows) { + windowAccountsInit = obj.windowsAccountsInit[obj.affectedWindows[aw]] + windowAccounts = obj.windowsAccounts[obj.affectedWindows[aw]] + if (windowAccounts.pendingSettlementCount !== windowAccountsInit.pendingSettlementCount || + windowAccounts.settledCount !== windowAccountsInit.settledCount) { + if (windowAccounts.pendingSettlementCount === 0 && + windowAccounts.notSettledCount === 0 && + windowAccounts.settledCount > 0) { + obj.allWindows[obj.affectedWindows[aw]].settlementWindowStateId = 'SETTLED' + obj.allWindows[obj.affectedWindows[aw]].reason = 'All setlement accounts are settled' + obj.allWindows[obj.affectedWindows[aw]].createdDate = obj.transactionTimestamp + settlementWindowStateChange.push(obj.allWindows[obj.affectedWindows[aw]]) + } + obj.settlementWindows.push(obj.allWindows[obj.affectedWindows[aw]]) + } + } + if (settlementWindowStateChange.length) { + insertPromises = [] + for (let swsc of settlementWindowStateChange) { + swsc.settlementWindowId = swsc.id + delete swsc.id // TODO: remote deletes + delete swsc.createDate + delete swsc.state + insertPromises.push( + knex('settlementWindowStateChange') + .insert(swsc).returning('settlementWindowStateChangeId') + .transacting(trx) + ) + } + let settlementWindowStateChangeIdList = (await Promise.all(insertPromises)).map(v => v[0]) + if (settlementWindowStateChangeIdList) { + updatePromises = [] + for (let i in settlementWindowStateChangeIdList) { + updatePromises.push( + knex('settlementWindow') + .where('settlementWindowId', settlementWindowStateChange[i].settlementWindowId) + .update({currentStateChangeId: settlementWindowStateChangeIdList[i]}) + .transacting(trx) + ) + } + await Promise.all(updatePromises) + } + } + // seq-settlement-6.2.5, step 43 + let settlementStateChange = [] + if (obj.settlementAccounts.settledCount !== obj.settlementAccountsInit.settledCount && + obj.settlementAccounts.pendingSettlementCount === 0 && + obj.settlementAccounts.notSettledCount === 0) { + obj.settlementData.settlementStateId = 'SETTLED' + obj.settlementData.reason = 'All setlement accounts are settled' + obj.settlementData.createdDate = obj.transactionTimestamp + settlementStateChange.push(obj.settlementData) + } + if (settlementStateChange.length) { + delete settlementStateChange[0].state + let settlementStateChangeId = await knex('settlementStateChange') + .insert(settlementStateChange).returning('settlementStateChangeId') + .transacting(trx) + await knex('settlement') + .where('settlementId', settlementStateChange[0].settlementId) + .update({currentStateChangeId: settlementStateChangeId[0]}) + .transacting(trx) + } + await trx.commit + return obj + } catch (err) { + await trx.rollback + throw err } }) } catch (err) { @@ -130,7 +222,7 @@ const Facade = { } }) await knex.batchInsert('settlementSettlementWindow', settlementSettlementWindowList).transacting(trx) - let settlementTransferParticipantIdList = await knex + /* let settlementTransferParticipantIdList = */await knex .from(knex.raw('settlementTransferParticipant (settlementId, settlementWindowId, participantCurrencyId, transferParticipantRoleTypeId, ledgerEntryTypeId, createdDate, amount)')) .insert(function () { this.from('settlementSettlementWindow AS ssw') @@ -210,7 +302,7 @@ const Facade = { .select('settlementWindowStateChangeId') .whereIn('settlementWindowId', idList) .andWhere('settlementWindowStateId', enums.settlementStates.PENDING_SETTLEMENT) - updatePromises = [] + updatePromises = [] for (let index in idList) { updatePromises.push(await knex('settlementWindow').transacting(trx) .where('settlementWindowId', idList[index]) diff --git a/src/models/settlement/index.js b/src/models/settlement/index.js index a020d546..acb51cce 100644 --- a/src/models/settlement/index.js +++ b/src/models/settlement/index.js @@ -22,6 +22,7 @@ * Gates Foundation - Name Surname + * Georgi Georgiev * Valentin Genev * Deon Botha * Rajiv Mothilal