fix: review
This commit is contained in:
parent
772805269f
commit
5045821593
9 changed files with 62 additions and 172 deletions
41
lib/commission-math.js
Normal file
41
lib/commission-math.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
const BN = require('./bn')
|
||||||
|
const configManager = require('./new-config-manager')
|
||||||
|
const coinUtils = require('./coin-utils')
|
||||||
|
|
||||||
|
function truncateCrypto (cryptoAtoms, cryptoCode) {
|
||||||
|
const DECIMAL_PLACES = 3
|
||||||
|
if (cryptoAtoms.eq(0)) return cryptoAtoms
|
||||||
|
|
||||||
|
const scale = 5 // TODO: change this to coins.displayScale when coins have that attribute
|
||||||
|
const scaleFactor = BN(10).pow(scale)
|
||||||
|
|
||||||
|
return BN(cryptoAtoms).truncated().div(scaleFactor)
|
||||||
|
.round(DECIMAL_PLACES).times(scaleFactor)
|
||||||
|
}
|
||||||
|
|
||||||
|
function fiatToCrypto (tx, rec, deviceId, config) {
|
||||||
|
const usableFiat = rec.fiat - rec.cashInFee
|
||||||
|
|
||||||
|
const commissions = configManager.getCommissions(tx.cryptoCode, deviceId, config)
|
||||||
|
const tickerRate = BN(tx.rawTickerPrice)
|
||||||
|
const discount = getDiscountRate(tx.discount, commissions[tx.direction])
|
||||||
|
const rate = tickerRate.mul(discount).round(5)
|
||||||
|
const unitScale = coinUtils.getCryptoCurrency(tx.cryptoCode).unitScale
|
||||||
|
const unitScaleFactor = BN(10).pow(unitScale)
|
||||||
|
|
||||||
|
return truncateCrypto(BN(usableFiat).div(rate.div(unitScaleFactor)), tx.cryptoCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDiscountRate (discount, commission) {
|
||||||
|
const bnDiscount = discount ? BN(discount) : BN(0)
|
||||||
|
const bnCommission = BN(commission)
|
||||||
|
const percentageDiscount = BN(1).sub(bnDiscount.div(100))
|
||||||
|
const percentageCommission = bnCommission.div(100)
|
||||||
|
return BN(1).add(percentageDiscount.mul(percentageCommission))
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
truncateCrypto,
|
||||||
|
fiatToCrypto,
|
||||||
|
getDiscountRate
|
||||||
|
}
|
||||||
|
|
@ -21,4 +21,9 @@ function softDeleteCoupon (couponId) {
|
||||||
return db.none(sql, [couponId])
|
return db.none(sql, [couponId])
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { getAvailableCoupons, getCoupon, createCoupon, softDeleteCoupon }
|
function getNumberOfAvailableCoupons () {
|
||||||
|
const sql = `SELECT COUNT(id) FROM coupons WHERE soft_deleted=false`
|
||||||
|
return db.one(sql).then(res => res.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { getAvailableCoupons, getCoupon, createCoupon, softDeleteCoupon, getNumberOfAvailableCoupons }
|
||||||
|
|
@ -13,7 +13,7 @@ const settingsLoader = require('../../new-settings-loader')
|
||||||
// const tokenManager = require('../../token-manager')
|
// const tokenManager = require('../../token-manager')
|
||||||
const blacklist = require('../../blacklist')
|
const blacklist = require('../../blacklist')
|
||||||
const machineEventsByIdBatch = require("../../postgresql_interface").machineEventsByIdBatch
|
const machineEventsByIdBatch = require("../../postgresql_interface").machineEventsByIdBatch
|
||||||
const couponManager = require('../../coupon-manager')
|
const couponManager = require('../../coupons')
|
||||||
|
|
||||||
const serverVersion = require('../../../package.json').version
|
const serverVersion = require('../../../package.json').version
|
||||||
|
|
||||||
|
|
@ -179,7 +179,6 @@ const typeDefs = gql`
|
||||||
id: ID!
|
id: ID!
|
||||||
code: String!
|
code: String!
|
||||||
discount: Int!
|
discount: Int!
|
||||||
soft_deleted: Boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Transaction {
|
type Transaction {
|
||||||
|
|
@ -295,7 +294,7 @@ const typeDefs = gql`
|
||||||
deleteBlacklistRow(cryptoCode: String!, address: String!): Blacklist
|
deleteBlacklistRow(cryptoCode: String!, address: String!): Blacklist
|
||||||
insertBlacklistRow(cryptoCode: String!, address: String!): Blacklist
|
insertBlacklistRow(cryptoCode: String!, address: String!): Blacklist
|
||||||
createCoupon(code: String!, discount: Int!): Coupon
|
createCoupon(code: String!, discount: Int!): Coupon
|
||||||
softDeleteCoupon(couponId: ID!): Coupon
|
deleteCoupon(couponId: ID!): Coupon
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -370,7 +369,7 @@ const resolvers = {
|
||||||
blacklist.insertIntoBlacklist(cryptoCode, address),
|
blacklist.insertIntoBlacklist(cryptoCode, address),
|
||||||
// revokeToken: (...[, { token }]) => tokenManager.revokeToken(token)
|
// revokeToken: (...[, { token }]) => tokenManager.revokeToken(token)
|
||||||
createCoupon: (...[, { code, discount }]) => couponManager.createCoupon(code, discount),
|
createCoupon: (...[, { code, discount }]) => couponManager.createCoupon(code, discount),
|
||||||
softDeleteCoupon: (...[, { couponId }]) => couponManager.softDeleteCoupon(couponId)
|
deleteCoupon: (...[, { couponId }]) => couponManager.deleteCoupon(couponId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ const cashOutHelper = require('./cash-out/cash-out-helper')
|
||||||
const machineLoader = require('./machine-loader')
|
const machineLoader = require('./machine-loader')
|
||||||
const customers = require('./customers')
|
const customers = require('./customers')
|
||||||
const coinUtils = require('./coin-utils')
|
const coinUtils = require('./coin-utils')
|
||||||
|
const commissionMath = require('./commission-math')
|
||||||
|
const coupons = require('./coupons')
|
||||||
|
|
||||||
const mapValuesWithKey = _.mapValues.convert({
|
const mapValuesWithKey = _.mapValues.convert({
|
||||||
cap: false
|
cap: false
|
||||||
|
|
@ -190,11 +192,6 @@ function plugins (settings, deviceId) {
|
||||||
.then(row => row.id)
|
.then(row => row.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNumberOfAvailableCoupons () {
|
|
||||||
const sql = `SELECT COUNT(id) FROM coupons WHERE soft_deleted=false`
|
|
||||||
return db.one(sql).then(res => res.count)
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapCoinSettings (coinParams) {
|
function mapCoinSettings (coinParams) {
|
||||||
const cryptoCode = coinParams[0]
|
const cryptoCode = coinParams[0]
|
||||||
const cryptoNetwork = coinParams[1]
|
const cryptoNetwork = coinParams[1]
|
||||||
|
|
@ -227,7 +224,7 @@ function plugins (settings, deviceId) {
|
||||||
const testnetPromises = cryptoCodes.map(c => wallet.cryptoNetwork(settings, c))
|
const testnetPromises = cryptoCodes.map(c => wallet.cryptoNetwork(settings, c))
|
||||||
const pingPromise = recordPing(deviceTime, machineVersion, machineModel)
|
const pingPromise = recordPing(deviceTime, machineVersion, machineModel)
|
||||||
const currentConfigVersionPromise = fetchCurrentConfigVersion()
|
const currentConfigVersionPromise = fetchCurrentConfigVersion()
|
||||||
const currentAvailableCoupons = getNumberOfAvailableCoupons()
|
const currentAvailableCoupons = coupons.getNumberOfAvailableCoupons()
|
||||||
|
|
||||||
const promises = [
|
const promises = [
|
||||||
buildAvailableCassettes(),
|
buildAvailableCassettes(),
|
||||||
|
|
@ -459,7 +456,7 @@ function plugins (settings, deviceId) {
|
||||||
function buyAndSell (rec, doBuy, tx) {
|
function buyAndSell (rec, doBuy, tx) {
|
||||||
const cryptoCode = rec.cryptoCode
|
const cryptoCode = rec.cryptoCode
|
||||||
const fiatCode = rec.fiatCode
|
const fiatCode = rec.fiatCode
|
||||||
const cryptoAtoms = doBuy ? fiatToCrypto(tx, rec) : rec.cryptoAtoms.neg()
|
const cryptoAtoms = doBuy ? commissionMath.fiatToCrypto(tx, rec, deviceId, settings.config) : rec.cryptoAtoms.neg()
|
||||||
|
|
||||||
const market = [fiatCode, cryptoCode].join('')
|
const market = [fiatCode, cryptoCode].join('')
|
||||||
|
|
||||||
|
|
@ -475,38 +472,6 @@ function plugins (settings, deviceId) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function truncateCrypto (cryptoAtoms, cryptoCode) {
|
|
||||||
const DECIMAL_PLACES = 3
|
|
||||||
if (cryptoAtoms.eq(0)) return cryptoAtoms
|
|
||||||
|
|
||||||
const scale = 5 // TODO: change this to coins.displayScale when coins have that attribute
|
|
||||||
const scaleFactor = BN(10).pow(scale)
|
|
||||||
|
|
||||||
return BN(cryptoAtoms).truncated().div(scaleFactor)
|
|
||||||
.round(DECIMAL_PLACES).times(scaleFactor)
|
|
||||||
}
|
|
||||||
|
|
||||||
function fiatToCrypto (tx, rec) {
|
|
||||||
const usableFiat = rec.fiat - rec.cashInFee
|
|
||||||
|
|
||||||
const commissions = configManager.getCommissions(tx.cryptoCode, deviceId, settings.config)
|
|
||||||
const tickerRate = BN(tx.rawTickerPrice)
|
|
||||||
const discount = getDiscountRate(tx.discount, commissions[tx.direction])
|
|
||||||
const rate = tickerRate.mul(discount).round(5)
|
|
||||||
const unitScale = coinUtils.getCryptoCurrency(tx.cryptoCode).unitScale
|
|
||||||
const unitScaleFactor = BN(10).pow(unitScale)
|
|
||||||
|
|
||||||
return truncateCrypto(BN(usableFiat).div(rate.div(unitScaleFactor)), tx.cryptoCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDiscountRate (discount, commission) {
|
|
||||||
const bnDiscount = discount ? BN(discount) : BN(0)
|
|
||||||
const bnCommission = BN(commission)
|
|
||||||
const percentageDiscount = BN(1).sub(bnDiscount.div(100))
|
|
||||||
const percentageCommission = bnCommission.div(100)
|
|
||||||
return BN(1).add(percentageDiscount.mul(percentageCommission))
|
|
||||||
}
|
|
||||||
|
|
||||||
function consolidateTrades (cryptoCode, fiatCode) {
|
function consolidateTrades (cryptoCode, fiatCode) {
|
||||||
const market = [fiatCode, cryptoCode].join('')
|
const market = [fiatCode, cryptoCode].join('')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,9 @@ const E = require('./error')
|
||||||
const customers = require('./customers')
|
const customers = require('./customers')
|
||||||
const logs = require('./logs')
|
const logs = require('./logs')
|
||||||
const compliance = require('./compliance')
|
const compliance = require('./compliance')
|
||||||
const couponManager = require('./coupon-manager')
|
const couponManager = require('./coupons')
|
||||||
const BN = require('./bn')
|
const BN = require('./bn')
|
||||||
|
const commissionMath = require('./commission-math')
|
||||||
|
|
||||||
const version = require('../package.json').version
|
const version = require('../package.json').version
|
||||||
|
|
||||||
|
|
@ -223,7 +224,7 @@ function verifyCoupon (req, res, next) {
|
||||||
const transaction = req.body.tx
|
const transaction = req.body.tx
|
||||||
const commissions = configManager.getCommissions(transaction.cryptoCode, req.deviceId, req.settings.config)
|
const commissions = configManager.getCommissions(transaction.cryptoCode, req.deviceId, req.settings.config)
|
||||||
const tickerRate = BN(transaction.rawTickerPrice)
|
const tickerRate = BN(transaction.rawTickerPrice)
|
||||||
const discount = getDiscountRate(coupon.discount, commissions[transaction.direction])
|
const discount = commissionMath.getDiscountRate(coupon.discount, commissions[transaction.direction])
|
||||||
const rates = {
|
const rates = {
|
||||||
[transaction.cryptoCode]: {
|
[transaction.cryptoCode]: {
|
||||||
[transaction.direction]: (transaction.direction === 'cashIn')
|
[transaction.direction]: (transaction.direction === 'cashIn')
|
||||||
|
|
@ -240,12 +241,6 @@ function verifyCoupon (req, res, next) {
|
||||||
.catch(next)
|
.catch(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDiscountRate (discount, commission) {
|
|
||||||
const percentageDiscount = BN(1).sub(BN(discount).div(100))
|
|
||||||
const percentageCommission = BN(commission).div(100)
|
|
||||||
return BN(1).add(percentageDiscount.mul(percentageCommission))
|
|
||||||
}
|
|
||||||
|
|
||||||
function addOrUpdateCustomer (req) {
|
function addOrUpdateCustomer (req) {
|
||||||
const customerData = req.body
|
const customerData = req.body
|
||||||
const machineVersion = req.query.version
|
const machineVersion = req.query.version
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ exports.up = function (next) {
|
||||||
code TEXT NOT NULL,
|
code TEXT NOT NULL,
|
||||||
discount SMALLINT NOT NULL,
|
discount SMALLINT NOT NULL,
|
||||||
soft_deleted BOOLEAN DEFAULT false )`,
|
soft_deleted BOOLEAN DEFAULT false )`,
|
||||||
`CREATE UNIQUE INDEX uq_code ON coupons USING btree(code) WHERE NOT soft_deleted`
|
`CREATE UNIQUE INDEX uq_code ON coupons (code) WHERE NOT soft_deleted`
|
||||||
]
|
]
|
||||||
|
|
||||||
db.multi(sql, next)
|
db.multi(sql, next)
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
const db = require('./db')
|
|
||||||
|
|
||||||
exports.up = function (next) {
|
|
||||||
var sql = [
|
|
||||||
'ALTER TABLE trades ADD COLUMN tx_in_id UUID UNIQUE',
|
|
||||||
'ALTER TABLE trades ADD CONSTRAINT fk_tx_in FOREIGN KEY (tx_in_id) REFERENCES cash_in_txs (id)',
|
|
||||||
'ALTER TABLE trades ADD COLUMN tx_out_id UUID UNIQUE',
|
|
||||||
'ALTER TABLE trades ADD CONSTRAINT fk_tx_out FOREIGN KEY (tx_in_id) REFERENCES cash_out_txs (id)'
|
|
||||||
]
|
|
||||||
|
|
||||||
db.multi(sql, next)
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.down = function (next) {
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
|
|
@ -21,14 +21,13 @@ const GET_COUPONS = gql`
|
||||||
id
|
id
|
||||||
code
|
code
|
||||||
discount
|
discount
|
||||||
soft_deleted
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const SOFT_DELETE_COUPON = gql`
|
const DELETE_COUPON = gql`
|
||||||
mutation softDeleteCoupon($couponId: ID!) {
|
mutation deleteCoupon($couponId: ID!) {
|
||||||
softDeleteCoupon(couponId: $couponId) {
|
deleteCoupon(couponId: $couponId) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +39,6 @@ const CREATE_COUPON = gql`
|
||||||
id
|
id
|
||||||
code
|
code
|
||||||
discount
|
discount
|
||||||
soft_deleted
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
@ -53,7 +51,7 @@ const Coupons = () => {
|
||||||
|
|
||||||
const { data: couponResponse, loading } = useQuery(GET_COUPONS)
|
const { data: couponResponse, loading } = useQuery(GET_COUPONS)
|
||||||
|
|
||||||
const [softDeleteCoupon] = useMutation(SOFT_DELETE_COUPON, {
|
const [softDeleteCoupon] = useMutation(DELETE_COUPON, {
|
||||||
refetchQueries: () => ['coupons']
|
refetchQueries: () => ['coupons']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
import { makeStyles } from '@material-ui/core'
|
|
||||||
import Grid from '@material-ui/core/Grid'
|
|
||||||
import React from 'react'
|
|
||||||
import {
|
|
||||||
Route,
|
|
||||||
Switch,
|
|
||||||
Redirect,
|
|
||||||
useLocation,
|
|
||||||
useHistory
|
|
||||||
} from 'react-router-dom'
|
|
||||||
|
|
||||||
import Sidebar from 'src/components/layout/Sidebar'
|
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
|
||||||
|
|
||||||
import CouponCodes from './CouponCodes'
|
|
||||||
import IndividualDiscounts from './IndividualDiscounts'
|
|
||||||
import LoyaltyDiscounts from './LoyaltyDiscounts'
|
|
||||||
|
|
||||||
const styles = {
|
|
||||||
grid: {
|
|
||||||
flex: 1,
|
|
||||||
height: '100%'
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
flex: 1,
|
|
||||||
marginLeft: 48,
|
|
||||||
paddingTop: 15
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
|
||||||
|
|
||||||
const innerRoutes = [
|
|
||||||
{
|
|
||||||
key: 'individual-discounts',
|
|
||||||
label: 'Individual Discounts',
|
|
||||||
route: '/compliance/loyalty/individual-discounts',
|
|
||||||
component: IndividualDiscounts
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'loyalty-discounts',
|
|
||||||
label: 'Loyalty Discounts',
|
|
||||||
route: '/compliance/loyalty/discounts',
|
|
||||||
component: LoyaltyDiscounts
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'coupon-codes',
|
|
||||||
label: 'Coupon Codes',
|
|
||||||
route: '/compliance/loyalty/coupons',
|
|
||||||
component: CouponCodes
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const Routes = ({ wizard }) => (
|
|
||||||
<Switch>
|
|
||||||
<Redirect
|
|
||||||
exact
|
|
||||||
from="/compliance/loyalty"
|
|
||||||
to="/compliance/loyalty/individual-discounts"
|
|
||||||
/>
|
|
||||||
<Route exact path="/" />
|
|
||||||
{innerRoutes.map(({ route, component: Page, key }) => (
|
|
||||||
<Route path={route} key={key}>
|
|
||||||
<Page name={key} wizard={wizard} />
|
|
||||||
</Route>
|
|
||||||
))}
|
|
||||||
</Switch>
|
|
||||||
)
|
|
||||||
|
|
||||||
const LoyaltyPanel = ({ wizard = false }) => {
|
|
||||||
const classes = useStyles()
|
|
||||||
const history = useHistory()
|
|
||||||
const location = useLocation()
|
|
||||||
|
|
||||||
const isSelected = it => location.pathname === it.route
|
|
||||||
|
|
||||||
const onClick = it => history.push(it.route)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<TitleSection title="Loyalty Panel"></TitleSection>
|
|
||||||
<Grid container className={classes.grid}>
|
|
||||||
<Sidebar
|
|
||||||
data={innerRoutes}
|
|
||||||
isSelected={isSelected}
|
|
||||||
displayName={it => it.label}
|
|
||||||
onClick={onClick}
|
|
||||||
/>
|
|
||||||
<div className={classes.content}>
|
|
||||||
<Routes wizard={wizard} />
|
|
||||||
</div>
|
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LoyaltyPanel
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue