Skip to content

Commit

Permalink
Merge pull request #278 from clark800/adjustments
Browse files Browse the repository at this point in the history
Add L_TRANSFER_ADJUSTMENTS table
  • Loading branch information
clark800 authored Jul 1, 2016
2 parents fc6fa08 + c2c9efd commit 43ad4f2
Show file tree
Hide file tree
Showing 14 changed files with 306 additions and 20 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"eslint": "^2.0.0",
"eslint-config-standard": "^5.1.0",
"eslint-plugin-promise": "^1.1.0",
"five-bells-integration-test": "^2.0.0",
"five-bells-integration-test": "^2.4.0",
"istanbul": "0.4.0",
"mocha": "~2.3.4",
"nock": "^2.10.0",
Expand Down
14 changes: 8 additions & 6 deletions src/lib/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ const path = require('path')
const connection = require('./knex').config.connection
const spawn = require('child_process').spawn
const knex = require('./knex').knex
const sequence = require('./utils').sequence
const readRejectionReasons = require('../models/db/rejectionReasons')
.readRejectionReasons
const readTransferStatuses = require('../models/db/transferStatuses')
.readTransferStatuses

const TABLE_NAMES = [
'L_TRANSFER_ADJUSTMENTS',
'L_ACCOUNTS',
'L_FULFILLMENTS',
'L_ENTRIES',
Expand All @@ -26,11 +28,6 @@ function withTransaction (callback) {
return knex.transaction(co.wrap(callback))
}

function sequence (promises) {
return promises.length === 0 ? Promise.resolve()
: promises[0].then(() => sequence(promises.slice(1)))
}

function executeStatements (sql) {
const separator = ';\n'
const statements = sql.split(separator)
Expand Down Expand Up @@ -84,9 +81,14 @@ function * dropTables () {
}

function * truncateTables () {
const dbType = knex.client.config.client
for (const tableName of TABLE_NAMES) {
if (!tableName.includes('_LU_')) {
yield knex(tableName).truncate().then()
if (dbType === 'pg') {
yield knex.raw('TRUNCATE TABLE "' + tableName + '" CASCADE;')
} else {
yield knex(tableName).truncate()
}
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/lib/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict'

function sequence (promises) {
return promises.length === 0 ? Promise.resolve()
: promises[0].then(() => sequence(promises.slice(1)))
}

module.exports = {
sequence
}
2 changes: 2 additions & 0 deletions src/models/converters/transfers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ function convertToInternalTransfer (data) {
if (data.id && data.id.startsWith('http')) {
data.id = uri.parse(data.id, 'transfer').id.toLowerCase()
}
data.debits = _.sortBy(data.debits, (debit) => debit.account)
data.credits = _.sortBy(data.credits, (credit) => credit.account)
for (let debit of data.debits) {
debit.account = uri.parse(debit.account, 'account').name.toLowerCase()
}
Expand Down
15 changes: 15 additions & 0 deletions src/models/db/accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const _ = require('lodash')
const db = require('./utils')(TABLE_NAME,
convertToPersistent, convertFromPersistent)
const config = require('../../services/config')
const knex = require('../../lib/knex').knex

function convertFromPersistent (data) {
data = _.cloneDeep(data)
Expand Down Expand Up @@ -66,6 +67,18 @@ function getAccountByFingerprint (fingerprint, options) {
return db.selectOne({FINGERPRINT: fingerprint}, options && options.transaction)
}

function getAccountById (id, options) {
return db.selectOne({ACCOUNT_ID: id}, options && options.transaction)
}

function getAccountId (name, options) {
const transaction = options && options.transaction
return (transaction || knex).from(TABLE_NAME).select()
.where('NAME', name).then((accounts) => {
return accounts.length === 1 ? accounts[0].ACCOUNT_ID : null
})
}

function adjustBalance (name, amount, options) {
const updateSQL =
'UPDATE "L_ACCOUNTS" SET "BALANCE" = "BALANCE" + ? WHERE "NAME" = ?'
Expand All @@ -89,8 +102,10 @@ function * upsertAccount (account, options) {
module.exports = {
getAccounts,
getConnectorAccounts,
getAccountId,
getAccount,
getAccountByFingerprint,
getAccountById,
adjustBalance,
updateAccount,
upsertAccount,
Expand Down
115 changes: 115 additions & 0 deletions src/models/db/adjustments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
'use strict'

const _ = require('lodash')
const assert = require('assert')
const TABLE_NAME = 'L_TRANSFER_ADJUSTMENTS'
const getAccountId = require('./accounts').getAccountId
const getAccountById = require('./accounts').getAccountById
const db = require('./utils')()

function isNil (x) {
return x === null || x === undefined
}

function convertFromPersistentAdjustment (data, options) {
return getAccountById(data.ACCOUNT_ID, options).then((account) => {
return _.omit({
account: account.name,
amount: Number(data.AMOUNT).toString(),
authorized: Boolean(data.IS_AUTHORIZED) || null,
memo: data.MEMO ? JSON.parse(data.MEMO) : null
}, (x) => isNil(x))
})
}

function convertFromPersistent (rows, options) {
const debits = Promise.all(rows.filter((row) => row.DEBIT_CREDIT === 'debit')
.map((debit) => convertFromPersistentAdjustment(debit, options)))
const credits = Promise.all(rows.filter((row) => row.DEBIT_CREDIT === 'credit')
.map((credit) => convertFromPersistentAdjustment(credit, options)))
return Promise.all([debits, credits]).then((results) => {
if (results[0].length === 0 && results[1].length === 0) {
return {}
}
return {
debits: _.sortBy(results[0], (adjustment) => adjustment.account),
credits: _.sortBy(results[1], (adjustment) => adjustment.account)
}
})
}

function convertToPersistentAdjustment (transferId, type, data, options) {
return getAccountId(data.account, options).then((accountId) => {
return _.omit({
TRANSFER_ID: transferId,
ACCOUNT_ID: accountId,
DEBIT_CREDIT: type,
AMOUNT: data.amount,
IS_AUTHORIZED: isNil(data.authorized) ? null : Number(data.authorized),
MEMO: data.memo ? JSON.stringify(data.memo) : null
}, (x) => isNil(x))
})
}

function convertToPersistent (data, options) {
const debits = Promise.all(data.debits.map((debit) =>
convertToPersistentAdjustment(data.id, 'debit', debit, options)))
const credits = Promise.all(data.credits.map((credit) =>
convertToPersistentAdjustment(data.id, 'credit', credit, options)))
return Promise.all([debits, credits]).then((results) =>
results[0].concat(results[1]))
}

function getAdjustments (transferId, options) {
return db.getTransaction(options).from(TABLE_NAME).select()
.where({TRANSFER_ID: transferId}).then((rows) => {
return convertFromPersistent(rows, options)
})
}

function insertAdjustments (transfer, options) {
return convertToPersistent(transfer, options).then((rows) => {
const transaction = db.getTransaction(options)
return Promise.all(
rows.map((row) => transaction.into(TABLE_NAME).insert(row)))
})
}

function _upsertAdjustment (persistentAdjustment, transaction) {
assert(transaction, 'transaction is required for upsert')
const where = {
TRANSFER_ID: persistentAdjustment.TRANSFER_ID,
ACCOUNT_ID: persistentAdjustment.ACCOUNT_ID,
DEBIT_CREDIT: persistentAdjustment.DEBIT_CREDIT
}
return transaction.from(TABLE_NAME)
.select().where(where).then((rows) => {
if (rows.length > 0) {
return transaction.into(TABLE_NAME).update(persistentAdjustment)
.where(where)
} else {
return transaction.into(TABLE_NAME).insert(persistentAdjustment)
}
})
}

function upsertAdjustment (persistentAdjustment, options) {
if (options && options.transaction) {
return _upsertAdjustment(persistentAdjustment, options.transaction)
} else {
return db.withTransaction((transaction) =>
_upsertAdjustment(persistentAdjustment, transaction))
}
}

function upsertAdjustments (transfer, options) {
return convertToPersistent(transfer, options).then((rows) => {
return Promise.all(rows.map((row) => upsertAdjustment(row, options)))
})
}

module.exports = {
getAdjustments,
insertAdjustments,
upsertAdjustments
}
26 changes: 20 additions & 6 deletions src/models/db/transfers.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const db = require('./utils')(TABLE_NAME,
const withTransaction = require('../../lib/db').withTransaction
const rejectionReasons = require('./rejectionReasons')
const transferStatuses = require('./transferStatuses')
const adjustments = require('./adjustments')

function convertFromPersistent (data) {
data = _.cloneDeep(data)
Expand All @@ -15,8 +16,6 @@ function convertFromPersistent (data) {
delete data.transfer_id
delete data.created_at
delete data.updated_at
data.credits = JSON.parse(data.credits)
data.debits = JSON.parse(data.debits)
data.additional_info = JSON.parse(data.additional_info)
if (data.expires_at) {
data.expires_at = new Date(data.expires_at)
Expand Down Expand Up @@ -46,8 +45,8 @@ function convertFromPersistent (data) {

function convertToPersistent (data) {
data = _.cloneDeep(data)
data.credits = JSON.stringify(data.credits)
data.debits = JSON.stringify(data.debits)
delete data.credits
delete data.debits
data.additional_info = JSON.stringify(data.additional_info)
if (data.proposed_at) {
data.proposed_at = new Date(data.proposed_at)
Expand Down Expand Up @@ -77,20 +76,35 @@ function convertToPersistent (data) {

function * getTransfer (id, options) {
return db.selectOne({TRANSFER_ID: id}, options && options.transaction)
.then((transfer) => {
return adjustments.getAdjustments(id, options).then((adjustments) => {
const result = _.assign({}, transfer, adjustments)
return _.isEmpty(result) ? null : result
})
})
}

function * updateTransfer (transfer, options) {
return db.update(transfer, {TRANSFER_ID: transfer.id},
options && options.transaction)
options && options.transaction).then((result) => {
return adjustments.upsertAdjustments(transfer, options).then(() => result)
})
}

function * insertTransfers (transfers, options) {
return db.insertAll(transfers, options && options.transaction)
.then(() => {
return Promise.all(transfers.map((transfer) => {
return adjustments.insertAdjustments(transfer, options)
}))
})
}

function * upsertTransfer (transfer, options) {
return db.upsert(transfer, {TRANSFER_ID: transfer.id},
options && options.transaction)
options && options.transaction).then((result) => {
return adjustments.upsertAdjustments(transfer, options).then(() => result)
})
}

module.exports = {
Expand Down
27 changes: 25 additions & 2 deletions src/sql/pg/create.sql
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ ALTER TABLE "L_LU_TRANSFER_STATUS" ADD CONSTRAINT "L_AK_LU_TRANSFER_STATUS"
CREATE TABLE IF NOT EXISTS "L_TRANSFERS" (
"TRANSFER_ID" CHARACTER VARYING(36) NOT NULL,
"LEDGER" CHARACTER VARYING(1024),
"DEBITS" CHARACTER VARYING(4000),
"CREDITS" CHARACTER VARYING(4000),
"ADDITIONAL_INFO" CHARACTER VARYING(4000),
"STATUS_ID" INTEGER NOT NULL REFERENCES "L_LU_TRANSFER_STATUS" ("STATUS_ID"),
"REJECTION_REASON_ID" INTEGER NULL
Expand Down Expand Up @@ -90,6 +88,31 @@ CREATE INDEX "L_XIF_TRANSFERS_REASON" ON "L_TRANSFERS"
("REJECTION_REASON_ID" ASC);


CREATE TABLE "L_TRANSFER_ADJUSTMENTS"
(
"TRANSFER_ADJUSTMENT_ID" SERIAL NOT NULL,
"TRANSFER_ID" CHARACTER VARYING(36) NOT NULL REFERENCES "L_TRANSFERS" ("TRANSFER_ID"),
"ACCOUNT_ID" INTEGER NOT NULL REFERENCES "L_ACCOUNTS" ("ACCOUNT_ID"),
"DEBIT_CREDIT" CHARACTER VARYING(10) NOT NULL,
"AMOUNT" DECIMAL(32,16) DEFAULT 0 NOT NULL,
"IS_AUTHORIZED" SMALLINT DEFAULT 0 NOT NULL,
"MEMO" CHARACTER VARYING(4000) NULL
);

CREATE INDEX "L_XPK_TRANSFER_ADJUSTMENTS" ON "L_TRANSFER_ADJUSTMENTS"
("TRANSFER_ADJUSTMENT_ID" ASC);
ALTER TABLE "L_TRANSFER_ADJUSTMENTS" ADD CONSTRAINT "L_PK_TRANSFER_ADJUSTMENTS"
PRIMARY KEY ("TRANSFER_ADJUSTMENT_ID");
CREATE UNIQUE INDEX "L_XAK_TRANSFER_ADJUSTMENTS" ON "L_TRANSFER_ADJUSTMENTS"
("TRANSFER_ID" ASC, "ACCOUNT_ID" ASC, "DEBIT_CREDIT" ASC);
CREATE INDEX "L_XIF_TRANSFER_ADJUSTMENTS_TRA" ON "L_TRANSFER_ADJUSTMENTS"
("TRANSFER_ID" ASC);
CREATE INDEX "L_XIF_TRANSFER_ADJUSTMENTS_ACC" ON "L_TRANSFER_ADJUSTMENTS"
("ACCOUNT_ID" ASC);
CREATE INDEX "L_XIE_TRANSFER_ADJUSTMENTS" ON "L_TRANSFER_ADJUSTMENTS"
("IS_AUTHORIZED" ASC);


CREATE TABLE IF NOT EXISTS "L_SUBSCRIPTIONS" (
"SUBSCRIPTION_ID" CHARACTER VARYING(36) NOT NULL,
"OWNER" CHARACTER VARYING(255) NOT NULL,
Expand Down
2 changes: 1 addition & 1 deletion src/sql/pg/drop.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- DROP TABLE IF EXISTS "L_TRANSFER_ADJUSTMENTS" CASCADE;
DROP TABLE IF EXISTS "L_TRANSFER_ADJUSTMENTS" CASCADE;
DROP TABLE IF EXISTS "L_ACCOUNTS" CASCADE;
DROP TABLE IF EXISTS "L_LU_REJECTION_REASON" CASCADE;
DROP TABLE IF EXISTS "L_LU_TRANSFER_STATUS" CASCADE;
Expand Down
16 changes: 14 additions & 2 deletions src/sql/sqlite3/create.sql
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ create unique index transfer_status_name on "L_LU_TRANSFER_STATUS"
create table if not exists "L_TRANSFERS" (
"TRANSFER_ID" char(36) not null primary key,
"LEDGER" varchar(1024),
"DEBITS" text,
"CREDITS" text,
"ADDITIONAL_INFO" text,
"STATUS_ID" integer not null,
"REJECTION_REASON_ID" integer,
Expand All @@ -58,6 +56,20 @@ create table if not exists "L_TRANSFERS" (
);


create table if not exists "L_TRANSFER_ADJUSTMENTS"
(
"TRANSFER_ADJUSTMENT_ID" integer not null primary key,
"TRANSFER_ID" varchar(36) not null,
"ACCOUNT_ID" integer not null,
"DEBIT_CREDIT" varchar(10) not null,
"AMOUNT" float DEFAULT 0 not null,
"IS_AUTHORIZED" boolean default 0 not null,
"MEMO" varchar(4000) null,
FOREIGN KEY("TRANSFER_ID") REFERENCES "L_TRANSFERS" ("TRANSFER_ID"),
FOREIGN KEY("ACCOUNT_ID") REFERENCES "L_ACCOUNTS" ("ACCOUNT_ID")
);


create table if not exists "L_SUBSCRIPTIONS" (
"SUBSCRIPTION_ID" char(36) not null primary key,
"OWNER" varchar(1024),
Expand Down
1 change: 1 addition & 0 deletions src/sql/sqlite3/drop.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
DROP TABLE IF EXISTS L_TRANSFER_ADJUSTMENTS;
DROP TABLE IF EXISTS L_ACCOUNTS;
DROP TABLE IF EXISTS L_LU_REJECTION_REASON;
DROP TABLE IF EXISTS L_LU_TRANSFER_STATUS;
Expand Down
Loading

0 comments on commit 43ad4f2

Please sign in to comment.