fix: change coupons to promo codes

This commit is contained in:
Sérgio Salgado 2021-02-02 13:58:01 +00:00 committed by Josh Harvey
parent 840afa866d
commit 59cb33570b
8 changed files with 87 additions and 88 deletions

View file

@ -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('../../coupons') const promoCodeManager = require('../../promo-codes')
const serverVersion = require('../../../package.json').version const serverVersion = require('../../../package.json').version
const transactions = require('../transactions') const transactions = require('../transactions')
@ -177,7 +177,7 @@ const typeDefs = gql`
ip_address: String ip_address: String
} }
type Coupon { type PromoCode {
id: ID! id: ID!
code: String! code: String!
discount: Int! discount: Int!
@ -276,7 +276,7 @@ const typeDefs = gql`
config: JSONObject config: JSONObject
blacklist: [Blacklist] blacklist: [Blacklist]
# userTokens: [UserToken] # userTokens: [UserToken]
coupons: [Coupon] promoCodes: [PromoCode]
cryptoRates: JSONObject cryptoRates: JSONObject
fiatRates: [Rate] fiatRates: [Rate]
} }
@ -310,8 +310,8 @@ const typeDefs = gql`
# revokeToken(token: String!): UserToken # revokeToken(token: String!): UserToken
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 createPromoCode(code: String!, discount: Int!): PromoCode
deleteCoupon(couponId: ID!): Coupon deletePromoCode(codeId: ID!): PromoCode
} }
` `
@ -362,7 +362,7 @@ const resolvers = {
accounts: () => settingsLoader.loadAccounts(), accounts: () => settingsLoader.loadAccounts(),
blacklist: () => blacklist.getBlacklist(), blacklist: () => blacklist.getBlacklist(),
// userTokens: () => tokenManager.getTokenList() // userTokens: () => tokenManager.getTokenList()
coupons: () => couponManager.getAvailableCoupons(), promoCodes: () => promoCodeManager.getAvailablePromoCodes(),
cryptoRates: () => cryptoRates: () =>
settingsLoader.loadLatest().then(settings => { settingsLoader.loadLatest().then(settings => {
const pi = plugins(settings) const pi = plugins(settings)
@ -396,8 +396,8 @@ const resolvers = {
insertBlacklistRow: (...[, { cryptoCode, address }]) => insertBlacklistRow: (...[, { cryptoCode, address }]) =>
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), createPromoCode: (...[, { code, discount }]) => promoCodeManager.createPromoCode(code, discount),
deleteCoupon: (...[, { couponId }]) => couponManager.deleteCoupon(couponId) deletePromoCode: (...[, { codeId }]) => promoCodeManager.deletePromoCode(codeId)
} }
} }

View file

@ -22,7 +22,7 @@ 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 commissionMath = require('./commission-math')
const coupons = require('./coupons') const promoCodes = require('./promo-codes')
const mapValuesWithKey = _.mapValues.convert({ const mapValuesWithKey = _.mapValues.convert({
cap: false cap: false
@ -224,13 +224,13 @@ 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 = coupons.getNumberOfAvailableCoupons() const currentAvailablePromoCodes = promoCodes.getNumberOfAvailablePromoCodes()
const promises = [ const promises = [
buildAvailableCassettes(), buildAvailableCassettes(),
pingPromise, pingPromise,
currentConfigVersionPromise currentConfigVersionPromise
].concat(tickerPromises, balancePromises, testnetPromises, currentAvailableCoupons) ].concat(tickerPromises, balancePromises, testnetPromises, currentAvailablePromoCodes)
return Promise.all(promises) return Promise.all(promises)
.then(arr => { .then(arr => {
@ -242,7 +242,7 @@ function plugins (settings, deviceId) {
const testNets = arr.slice(2 * cryptoCodesCount + 3, arr.length - 1) const testNets = arr.slice(2 * cryptoCodesCount + 3, arr.length - 1)
const coinParams = _.zip(cryptoCodes, testNets) const coinParams = _.zip(cryptoCodes, testNets)
const coinsWithoutRate = _.map(mapCoinSettings, coinParams) const coinsWithoutRate = _.map(mapCoinSettings, coinParams)
const areThereAvailableCoupons = arr[arr.length - 1] > 0 const areThereAvailablePromoCodes = arr[arr.length - 1] > 0
return { return {
cassettes, cassettes,
@ -250,7 +250,7 @@ function plugins (settings, deviceId) {
balances: buildBalances(balances), balances: buildBalances(balances),
coins: _.zipWith(_.assign, coinsWithoutRate, tickers), coins: _.zipWith(_.assign, coinsWithoutRate, tickers),
configVersion, configVersion,
areThereAvailableCoupons areThereAvailablePromoCodes
} }
}) })
} }

View file

@ -1,29 +1,29 @@
const db = require('./db') const db = require('./db')
const uuid = require('uuid') const uuid = require('uuid')
function getAvailableCoupons () { function getAvailablePromoCodes () {
const sql = `SELECT * FROM coupons WHERE soft_deleted=false` const sql = `SELECT * FROM coupons WHERE soft_deleted=false`
return db.any(sql) return db.any(sql)
} }
function getCoupon (code) { function getPromoCode (code) {
const sql = `SELECT * FROM coupons WHERE code=$1 AND soft_deleted=false` const sql = `SELECT * FROM coupons WHERE code=$1 AND soft_deleted=false`
return db.oneOrNone(sql, [code]) return db.oneOrNone(sql, [code])
} }
function createCoupon (code, discount) { function createPromoCode (code, discount) {
const sql = `INSERT INTO coupons (id, code, discount) VALUES ($1, $2, $3) RETURNING *` const sql = `INSERT INTO coupons (id, code, discount) VALUES ($1, $2, $3) RETURNING *`
return db.one(sql, [uuid.v4(), code, discount]) return db.one(sql, [uuid.v4(), code, discount])
} }
function deleteCoupon (couponId) { function deletePromoCode (id) {
const sql = `UPDATE coupons SET soft_deleted=true WHERE id=$1` const sql = `UPDATE coupons SET soft_deleted=true WHERE id=$1`
return db.none(sql, [couponId]) return db.none(sql, [id])
} }
function getNumberOfAvailableCoupons () { function getNumberOfAvailablePromoCodes () {
const sql = `SELECT COUNT(id) FROM coupons WHERE soft_deleted=false` const sql = `SELECT COUNT(id) FROM coupons WHERE soft_deleted=false`
return db.one(sql).then(res => res.count) return db.one(sql).then(res => res.count)
} }
module.exports = { getAvailableCoupons, getCoupon, createCoupon, deleteCoupon, getNumberOfAvailableCoupons } module.exports = { getAvailablePromoCodes, getPromoCode, createPromoCode, deletePromoCode, getNumberOfAvailablePromoCodes }

View file

@ -25,7 +25,7 @@ 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('./coupons') const promoCodes = require('./promo-codes')
const BN = require('./bn') const BN = require('./bn')
const commissionMath = require('./commission-math') const commissionMath = require('./commission-math')
@ -216,15 +216,15 @@ function verifyTx (req, res, next) {
.catch(next) .catch(next)
} }
function verifyCoupon (req, res, next) { function verifyPromoCode (req, res, next) {
couponManager.getCoupon(req.body.codeInput) promoCodes.getPromoCode(req.body.codeInput)
.then(coupon => { .then(promoCode => {
if (!coupon) return next() if (!promoCode) return 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 = commissionMath.getDiscountRate(coupon.discount, commissions[transaction.direction]) const discount = commissionMath.getDiscountRate(promoCode.discount, commissions[transaction.direction])
const rates = { const rates = {
[transaction.cryptoCode]: { [transaction.cryptoCode]: {
[transaction.direction]: (transaction.direction === 'cashIn') [transaction.direction]: (transaction.direction === 'cashIn')
@ -234,7 +234,7 @@ function verifyCoupon (req, res, next) {
} }
respond(req, res, { respond(req, res, {
coupon: coupon, promoCode: promoCode,
newRates: rates newRates: rates
}) })
}) })
@ -479,7 +479,7 @@ const configRequiredRoutes = [
'/phone_code', '/phone_code',
'/customer', '/customer',
'/tx', '/tx',
'/verify_coupon' '/verify_promo_code'
] ]
const app = express() const app = express()
@ -506,7 +506,7 @@ app.post('/state', stateChange)
app.post('/verify_user', verifyUser) app.post('/verify_user', verifyUser)
app.post('/verify_transaction', verifyTx) app.post('/verify_transaction', verifyTx)
app.post('/verify_coupon', verifyCoupon) app.post('/verify_promo_code', verifyPromoCode)
app.post('/phone_code', getCustomerWithPhoneCode) app.post('/phone_code', getCustomerWithPhoneCode)
app.patch('/customer/:id', updateCustomer) app.patch('/customer/:id', updateCustomer)

View file

@ -10,17 +10,17 @@ import DataTable from 'src/components/tables/DataTable'
import { H2, TL1 } from 'src/components/typography' import { H2, TL1 } from 'src/components/typography'
import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg' import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg'
import styles from './CouponCodes.styles' import styles from './PromoCodes.styles'
import CouponCodesModal from './CouponCodesModal' import PromoCodesModal from './PromoCodesModal'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const DUPLICATE_ERROR_MSG = 'There is already a coupon with that code!' const DUPLICATE_ERROR_MSG = 'There is already a promotion with that code!'
const DEFAULT_ERROR_MSG = 'Failed to save' const DEFAULT_ERROR_MSG = 'Failed to save'
const GET_COUPONS = gql` const GET_PROMO_CODES = gql`
query coupons { query promoCodes {
coupons { promoCodes {
id id
code code
discount discount
@ -28,17 +28,17 @@ const GET_COUPONS = gql`
} }
` `
const DELETE_COUPON = gql` const DELETE_CODE = gql`
mutation deleteCoupon($couponId: ID!) { mutation deletePromoCode($codeId: ID!) {
deleteCoupon(couponId: $couponId) { deletePromoCodes(codeId: $codeId) {
id id
} }
} }
` `
const CREATE_COUPON = gql` const CREATE_CODE = gql`
mutation createCoupon($code: String!, $discount: Int!) { mutation createPromoCode($code: String!, $discount: Int!) {
createCoupon(code: $code, discount: $discount) { createPromoCode(code: $code, discount: $discount) {
id id
code code
discount discount
@ -46,38 +46,36 @@ const CREATE_COUPON = gql`
} }
` `
const Coupons = () => { const PromoCodes = () => {
const classes = useStyles() const classes = useStyles()
const [showModal, setShowModal] = useState(false) const [showModal, setShowModal] = useState(false)
const [errorMsg, setErrorMsg] = useState(null) const [errorMsg, setErrorMsg] = useState(null)
const toggleModal = () => setShowModal(!showModal) const toggleModal = () => setShowModal(!showModal)
const { data: couponResponse, loading } = useQuery(GET_COUPONS) const { data: codeResponse, loading } = useQuery(GET_PROMO_CODES)
const [deleteCoupon] = useMutation(DELETE_COUPON, { const [deleteCode] = useMutation(DELETE_CODE, {
refetchQueries: () => ['coupons'] refetchQueries: () => ['promoCodes']
}) })
const [createCoupon] = useMutation(CREATE_COUPON, { const [createCode] = useMutation(CREATE_CODE, {
refetchQueries: () => ['coupons'] refetchQueries: () => ['promoCodes']
}) })
const addCoupon = (code, discount) => { const addCode = (code, discount) => {
setErrorMsg(null) setErrorMsg(null)
createCoupon({ createCode({
variables: { code: code, discount: discount } variables: { code: code, discount: discount }
}) })
.then(res => { .then(res => {
if (!res.errors) return setShowModal(false) if (!res.errors) return setShowModal(false)
const duplicateCouponError = R.any(it => const duplicateCodeError = R.any(it =>
R.includes('duplicate', it?.message) R.includes('duplicate', it?.message)
)(res.errors) )(res.errors)
const msg = duplicateCouponError const msg = duplicateCodeError ? DUPLICATE_ERROR_MSG : DEFAULT_ERROR_MSG
? DUPLICATE_ERROR_MSG
: DEFAULT_ERROR_MSG
setErrorMsg(msg) setErrorMsg(msg)
}) })
.catch(err => { .catch(err => {
@ -88,7 +86,7 @@ const Coupons = () => {
const elements = [ const elements = [
{ {
header: 'Coupon Code', header: 'Code',
width: 300, width: 300,
textAlign: 'left', textAlign: 'left',
size: 'sm', size: 'sm',
@ -113,7 +111,7 @@ const Coupons = () => {
view: t => ( view: t => (
<IconButton <IconButton
onClick={() => { onClick={() => {
deleteCoupon({ variables: { couponId: t.id } }) deleteCode({ variables: { codeId: t.id } })
}}> }}>
<DeleteIcon /> <DeleteIcon />
</IconButton> </IconButton>
@ -123,8 +121,8 @@ const Coupons = () => {
return ( return (
<> <>
<TitleSection title="Discount Coupons"></TitleSection> <TitleSection title="Promo Codes"></TitleSection>
{!loading && !R.isEmpty(couponResponse.coupons) && ( {!loading && !R.isEmpty(codeResponse.promoCodes) && (
<Box <Box
marginBottom={4} marginBottom={4}
marginTop={-5} marginTop={-5}
@ -132,32 +130,32 @@ const Coupons = () => {
display="flex" display="flex"
justifyContent="flex-end"> justifyContent="flex-end">
<Link color="primary" onClick={toggleModal}> <Link color="primary" onClick={toggleModal}>
Add new coupon Add new code
</Link> </Link>
</Box> </Box>
)} )}
{!loading && !R.isEmpty(couponResponse.coupons) && ( {!loading && !R.isEmpty(codeResponse.promoCodes) && (
<DataTable <DataTable
elements={elements} elements={elements}
data={R.path(['coupons'])(couponResponse)} data={R.path(['promoCodes'])(codeResponse)}
/> />
)} )}
{!loading && R.isEmpty(couponResponse.coupons) && ( {!loading && R.isEmpty(codeResponse.promoCodes) && (
<Box display="flex" alignItems="left" flexDirection="column"> <Box display="flex" alignItems="left" flexDirection="column">
<H2>Currently, there are no active coupon codes on your network.</H2> <H2>Currently, there are no active promo codes on your network.</H2>
<Button onClick={toggleModal}>Add coupon</Button> <Button onClick={toggleModal}>Add Code</Button>
</Box> </Box>
)} )}
<CouponCodesModal <PromoCodesModal
showModal={showModal} showModal={showModal}
onClose={() => { onClose={() => {
setErrorMsg(null) setErrorMsg(null)
setShowModal(false) setShowModal(false)
}} }}
errorMsg={errorMsg} errorMsg={errorMsg}
addCoupon={addCoupon} addCode={addCode}
/> />
</> </>
) )
} }
export default Coupons export default PromoCodes

View file

@ -11,7 +11,7 @@ import { Button } from 'src/components/buttons'
import { TextInput, NumberInput } from 'src/components/inputs/formik' import { TextInput, NumberInput } from 'src/components/inputs/formik'
import { H3, TL1, P } from 'src/components/typography' import { H3, TL1, P } from 'src/components/typography'
import styles from './CouponCodes.styles' import styles from './PromoCodes.styles'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
@ -31,18 +31,18 @@ const validationSchema = Yup.object().shape({
.max(100) .max(100)
}) })
const CouponCodesModal = ({ showModal, onClose, errorMsg, addCoupon }) => { const PromoCodesModal = ({ showModal, onClose, errorMsg, addCode }) => {
const classes = useStyles() const classes = useStyles()
const handleAddCoupon = (code, discount) => { const handleAddCode = (code, discount) => {
addCoupon(R.toUpper(code), parseInt(discount)) addCode(R.toUpper(code), parseInt(discount))
} }
return ( return (
<> <>
{showModal && ( {showModal && (
<Modal <Modal
title="Add coupon code discount" title="Add promo code discount"
closeOnBackdropClick={true} closeOnBackdropClick={true}
width={600} width={600}
height={500} height={500}
@ -52,10 +52,10 @@ const CouponCodesModal = ({ showModal, onClose, errorMsg, addCoupon }) => {
initialValues={initialValues} initialValues={initialValues}
validationSchema={validationSchema} validationSchema={validationSchema}
onSubmit={({ code, discount }) => { onSubmit={({ code, discount }) => {
handleAddCoupon(code, discount) handleAddCode(code, discount)
}}> }}>
<Form id="coupon-form" className={classes.form}> <Form id="promo-form" className={classes.form}>
<H3 className={classes.modalLabel1}>Coupon code name</H3> <H3 className={classes.modalLabel1}>Promo code name</H3>
<Field <Field
name="code" name="code"
autoFocus autoFocus
@ -69,13 +69,14 @@ const CouponCodesModal = ({ showModal, onClose, errorMsg, addCoupon }) => {
<H3 className={classes.modalLabel2}>Define discount rate</H3> <H3 className={classes.modalLabel2}>Define discount rate</H3>
<Tooltip width={304}> <Tooltip width={304}>
<P> <P>
The discount rate inserted will be applied to the This is a percentage discount off of your existing
commissions of all transactions performed with this commission rates for a customer entering this code at the
respective coupon code. machine.
</P> </P>
<P> <P>
(It should be a number between 0 (zero) and 100 (one For instance, if you charge 8% commissions, and this code is
hundred)). set for 50%, then you'll instead be charging 4% on
transactions using the code.
</P> </P>
</Tooltip> </Tooltip>
</div> </div>
@ -97,9 +98,9 @@ const CouponCodesModal = ({ showModal, onClose, errorMsg, addCoupon }) => {
{errorMsg && <ErrorMessage>{errorMsg}</ErrorMessage>} {errorMsg && <ErrorMessage>{errorMsg}</ErrorMessage>}
<Button <Button
type="submit" type="submit"
form="coupon-form" form="promo-form"
className={classes.submit}> className={classes.submit}>
Add coupon Add code
</Button> </Button>
</div> </div>
</Form> </Form>
@ -110,4 +111,4 @@ const CouponCodesModal = ({ showModal, onClose, errorMsg, addCoupon }) => {
) )
} }
export default CouponCodesModal export default PromoCodesModal

View file

@ -22,7 +22,7 @@ import { Customers, CustomerProfile } from 'src/pages/Customers'
import Dashboard from 'src/pages/Dashboard' import Dashboard from 'src/pages/Dashboard'
import Funding from 'src/pages/Funding' import Funding from 'src/pages/Funding'
import Locales from 'src/pages/Locales' import Locales from 'src/pages/Locales'
import Coupons from 'src/pages/LoyaltyPanel/CouponCodes' import PromoCodes from 'src/pages/LoyaltyPanel/PromoCodes'
import MachineLogs from 'src/pages/MachineLogs' import MachineLogs from 'src/pages/MachineLogs'
import Machines from 'src/pages/Machines' import Machines from 'src/pages/Machines'
import CashCassettes from 'src/pages/Maintenance/CashCassettes' import CashCassettes from 'src/pages/Maintenance/CashCassettes'
@ -212,10 +212,10 @@ const tree = [
component: Blacklist component: Blacklist
}, },
{ {
key: 'discount-coupons', key: 'promo-codes',
label: 'Discount Coupons', label: 'Promo Codes',
route: '/compliance/loyalty/coupons', route: '/compliance/loyalty/codes',
component: Coupons component: PromoCodes
}, },
{ {
key: 'customer', key: 'customer',