diff --git a/lib/customers.js b/lib/customers.js index cb7c588f..4279854b 100644 --- a/lib/customers.js +++ b/lib/customers.js @@ -562,13 +562,13 @@ function getCustomerById (id) { phone, phone_at, email, email_at, phone_override, sms_override, id_card_data_at, id_card_data, id_card_data_override, id_card_data_expiration, id_card_photo_path, id_card_photo_at, id_card_photo_override, us_ssn_at, us_ssn, us_ssn_override, sanctions, sanctions_at, sanctions_override, total_txs, total_spent, GREATEST(created, last_transaction, last_auth_attempt, last_data_provided) AS last_active, fiat AS last_tx_fiat, - fiat_code AS last_tx_fiat_code, tx_class AS last_tx_class, subscriber_info, subscriber_info_at, custom_fields, notes, is_test_customer + fiat_code AS last_tx_fiat_code, tx_class AS last_tx_class, subscriber_info, subscriber_info_at, custom_fields, notes, is_test_customer, last_used_machine FROM ( SELECT c.id, c.authorized_override, greatest(0, date_part('day', c.suspended_until - now())) AS days_suspended, GREATEST(c.phone_at, c.email_at, c.id_card_data_at, c.front_camera_at, c.id_card_photo_at, c.us_ssn_at) AS last_data_provided, c.suspended_until > now() AS is_suspended, - c.front_camera_path, c.front_camera_override, c.front_camera_at, c.last_auth_attempt, + c.front_camera_path, c.front_camera_override, c.front_camera_at, c.last_auth_attempt, c.last_used_machine, c.phone, c.phone_at, c.email, c.email_at, c.phone_override, c.sms_override, c.id_card_data, c.id_card_data_at, c.id_card_data_override, c.id_card_data_expiration, c.id_card_photo_path, c.id_card_photo_at, c.id_card_photo_override, c.us_ssn, c.us_ssn_at, c.us_ssn_override, c.sanctions, c.sanctions_at, c.sanctions_override, c.subscriber_info, c.subscriber_info_at, c.is_test_customer, c.created, t.tx_class, t.fiat, t.fiat_code, t.created as last_transaction, cn.notes, @@ -724,11 +724,11 @@ function updatePhotoCard (id, patch) { function updatePhotos (imagesData, id, dir) { return Promise.resolve(imagesData) - .then(patch => { - if (_.isEmpty(imagesData)) { - return patch - } + .then(imagesData => { const newPatch = {} + if (_.isEmpty(imagesData)) { + return newPatch + } // i.e. ..////idcarddata const dirname = path.join(dir) // create the directory tree if needed @@ -764,7 +764,6 @@ function updateIdCardData (patch, id) { .then(patch => { const imagesData = _.get('photos', patch) return updatePhotos(imagesData, id, directory) - .then(newPatch => newPatch) .catch(err => logger.error('while saving the image: ', err)) }) } @@ -931,9 +930,9 @@ function disableTestCustomer (customerId) { return db.none(sql, [customerId]) } -function updateLastAuthAttempt (customerId) { - const sql = `UPDATE customers SET last_auth_attempt=NOW() WHERE id=$1` - return db.none(sql, [customerId]) +function updateLastAuthAttempt (customerId, deviceId) { + const sql = `UPDATE customers SET last_auth_attempt=NOW(), last_used_machine=$2 WHERE id=$1` + return db.none(sql, [customerId, deviceId]) } function getExternalComplianceMachine (customer) { diff --git a/lib/machine-loader.js b/lib/machine-loader.js index d9113eb5..fe08f15e 100644 --- a/lib/machine-loader.js +++ b/lib/machine-loader.js @@ -148,7 +148,7 @@ function getMachineNames (config) { * @returns {string} machine name */ function getMachineName (machineId) { - const sql = 'SELECT * FROM devices WHERE device_id=$1' + const sql = 'SELECT name FROM devices WHERE device_id=$1' return db.oneOrNone(sql, [machineId]) .then(it => it.name) } diff --git a/lib/new-admin/graphql/resolvers/customer.resolver.js b/lib/new-admin/graphql/resolvers/customer.resolver.js index 93695b7e..98d40d44 100644 --- a/lib/new-admin/graphql/resolvers/customer.resolver.js +++ b/lib/new-admin/graphql/resolvers/customer.resolver.js @@ -3,15 +3,19 @@ const anonymous = require('../../../constants').anonymousCustomer const customers = require('../../../customers') const filters = require('../../filters') const customerNotes = require('../../../customer-notes') +const machineLoader = require('../../../machine-loader') + +const addLastUsedMachineName = customer => + (customer.lastUsedMachine ? machineLoader.getMachineName(customer.lastUsedMachine) : Promise.resolve(null)) + .then(lastUsedMachineName => Object.assign(customer, { lastUsedMachineName })) const resolvers = { - Customer: { isAnonymous: parent => (parent.customerId === anonymous.uuid) }, Query: { customers: (...[, { phone, email, name, address, id }]) => customers.getCustomersList(phone, name, address, id, email), - customer: (...[, { customerId }]) => customers.getCustomerById(customerId), + customer: (...[, { customerId }]) => customers.getCustomerById(customerId).then(addLastUsedMachineName), customerFilters: () => filters.customer() }, Mutation: { diff --git a/lib/new-admin/graphql/types/customer.type.js b/lib/new-admin/graphql/types/customer.type.js index fe140032..80b5e2eb 100644 --- a/lib/new-admin/graphql/types/customer.type.js +++ b/lib/new-admin/graphql/types/customer.type.js @@ -33,6 +33,8 @@ const typeDef = gql` lastTxFiat: String lastTxFiatCode: String lastTxClass: String + lastUsedMachine: String + lastUsedMachineName: String transactions: [Transaction] subscriberInfo: JSONObject phoneOverride: String diff --git a/lib/routes/customerRoutes.js b/lib/routes/customerRoutes.js index 0ebb4128..3a00fe6e 100644 --- a/lib/routes/customerRoutes.js +++ b/lib/routes/customerRoutes.js @@ -257,7 +257,7 @@ function getExternalComplianceLink (req, res, next) { .then(url => respond(req, res, { url })) } -function addOrUpdateCustomer (customerData, config, isEmailAuth) { +function addOrUpdateCustomer (customerData, deviceId, config, isEmailAuth) { const triggers = configManager.getTriggers(config) const maxDaysThreshold = complianceTriggers.maxDaysThreshold(triggers) @@ -273,7 +273,7 @@ function addOrUpdateCustomer (customerData, config, isEmailAuth) { }) .then(customer => customers.getById(customer.id)) .then(customer => { - customers.updateLastAuthAttempt(customer.id).catch(() => { + customers.updateLastAuthAttempt(customer.id, deviceId).catch(() => { logger.info('failure updating last auth attempt for customer ', customer.id) }) return customer @@ -292,14 +292,15 @@ function addOrUpdateCustomer (customerData, config, isEmailAuth) { } function getOrAddCustomerPhone (req, res, next) { + const deviceId = req.deviceId const customerData = req.body - const pi = plugins(req.settings, req.deviceId) + const pi = plugins(req.settings, deviceId) const phone = req.body.phone return pi.getPhoneCode(phone) .then(code => { - return addOrUpdateCustomer(customerData, req.settings.config, false) + return addOrUpdateCustomer(customerData, deviceId, req.settings.config, false) .then(customer => respond(req, res, { code, customer })) }) .catch(err => { diff --git a/migrations/1721313145136-customer-last-used-machine.js b/migrations/1721313145136-customer-last-used-machine.js new file mode 100644 index 00000000..119f0bf8 --- /dev/null +++ b/migrations/1721313145136-customer-last-used-machine.js @@ -0,0 +1,9 @@ +const db = require('./db') + +exports.up = function (next) { + db.multi(['ALTER TABLE customers ADD COLUMN last_used_machine TEXT REFERENCES devices (device_id)'], next) +} + +exports.down = function (next) { + next() +} diff --git a/new-lamassu-admin/src/pages/Customers/CustomerProfile.js b/new-lamassu-admin/src/pages/Customers/CustomerProfile.js index cc8c54bf..d9256f3b 100644 --- a/new-lamassu-admin/src/pages/Customers/CustomerProfile.js +++ b/new-lamassu-admin/src/pages/Customers/CustomerProfile.js @@ -74,6 +74,7 @@ const GET_CUSTOMER = gql` totalTxs totalSpent lastActive + lastUsedMachineName lastTxFiat lastTxFiatCode lastTxClass diff --git a/new-lamassu-admin/src/pages/Customers/components/TransactionsList.js b/new-lamassu-admin/src/pages/Customers/components/TransactionsList.js index eb8a189a..513ab311 100644 --- a/new-lamassu-admin/src/pages/Customers/components/TransactionsList.js +++ b/new-lamassu-admin/src/pages/Customers/components/TransactionsList.js @@ -21,6 +21,7 @@ const TransactionsList = ({ customer, data, loading }) => { const classes = useStyles() const LastTxIcon = customer.lastTxClass === 'cashOut' ? TxOutIcon : TxInIcon const hasData = !(R.isEmpty(data) || R.isNil(data)) + const { lastUsedMachineName } = customer const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone const tableSpacingClasses = { @@ -65,6 +66,11 @@ const TransactionsList = ({ customer, data, loading }) => { ${customer.lastTxFiatCode}`} ) + }, + { + header: 'Last used machine', + size: 198, + value: ifNotNull(lastUsedMachineName, <>{lastUsedMachineName}) } ]