feat: adjustment implemented to support template for CRM SAP Gradezco client

master
adriano 2025-10-13 10:54:49 -03:00
parent a9ee98449b
commit b457e3088d
13 changed files with 241 additions and 89 deletions

90
.gitignore vendored
View File

@ -9,6 +9,96 @@
**/backend/build **/backend/build
**/frontend/dist **/frontend/dist
**/frontend/build
/node_modules /node_modules
# ===============================
# Node / React.js base
# ===============================
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
package-lock.json
yarn.lock
.pnpm-debug.log
# ===============================
# Build output
# ===============================
build/
dist/
.out/
.cache/
.temp/
# ===============================
# Environment variables
# ===============================
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# ===============================
# Logs
# ===============================
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# ===============================
# Editor / IDE
# ===============================
.vscode/
.idea/
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# ===============================
# OS files
# ===============================
.DS_Store
Thumbs.db
# ===============================
# Test coverage / reports
# ===============================
coverage/
*.lcov
# ===============================
# Misc
# ===============================
*.bak
*.tmp
*.old
*.orig
# ===============================
# Local development tools
# ===============================
/.eslintcache
/.stylelintcache
/.parcel-cache
/.next/
.storybook-out/
storybook-static/
# ===============================
# React Native (if any hybrid code)
# ===============================
.expo/
android/
ios/

View File

@ -3,7 +3,7 @@
"type": "basic", "type": "basic",
"userName": "_INTEGRACAO_", "userName": "_INTEGRACAO_",
"passWord": "p3G44K10366", "passWord": "p3G44K10366",
"crmPhoneTest":"+5514987659932", "crmPhoneTest":"+5511998765123",
"crmAccountId":"1000320" "crmAccountId":"1000320"
}, },
"crmRest":[ "crmRest":[
@ -23,7 +23,7 @@
"AccountID":"1000320" "AccountID":"1000320"
}, },
"response":{ "response":{
"id":"d.results.ObjectID" "id":"d.results.ContactID"
} }
} }
}, },
@ -34,13 +34,72 @@
"requestEncoding":"Json", "requestEncoding":"Json",
"requestType":"Get", "requestType":"Get",
"responseType":"Json", "responseType":"Json",
"url":"https://my365398.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/ContactCollection?%24select=ObjectID%2CFirstName%2CLastName%2CEmail%2CStatusCode%2CStatusCodeText%2CPhone%2CMobile%2CNormalisedMobile&%24filter=NormalisedMobile%20eq%20%27%2BcrmPhone%27" "url":"https://my365398.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/ContactCollection?$select=ObjectID,FirstName,LastName,Email,StatusCode,StatusCodeText,Phone,Mobile,NormalisedMobile,ContactID&$filter=NormalisedMobile eq '%2BcrmPhone'"
}, },
"response":{ "response":{
"phone":"data.d.results[0].NormalisedMobile", "phone":"d.results[0].NormalisedMobile",
"id":"data.results[0].ObjectID" "id":"d.results[0].ContactID",
"objectId":"d.results[0].ObjectID"
} }
} }
} },
{
"callJournaling":{
"request":{
"requestContentType":"application/json",
"requestEncoding":"Json",
"requestType":"Post",
"responseType":"Json",
"url":"https://my365398.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/PhoneCallCollection"
},
"calls":[
{
"inboundAnsweredCall":{
"Subject": "Ligação recebida",
"Status": "1",
"InitiatorCode": "2",
"DataOriginTypeCode": "3",
"MainAccountPartyID": "1000320",
"MainContactPartyID":"crmContactId"
}
},
{
"outboundAnsweredCall":{
"Subject": "Ligação Realizada",
"Status": "1",
"InitiatorCode": "3",
"DataOriginTypeCode": "3",
"MainAccountPartyID": "1000320",
"MainContactPartyID":"crmContactId"
}
}
]
}
},
{
"chatJournaling":{
"request":{
"requestContentType":"application/json",
"requestEncoding":"Json",
"requestType":"Post",
"responseType":"Json",
"url":"https://my365398.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/ContactCollection('contactObjectId')/ContactTextCollection"
},
"chats":[
{
"chatDone":{
"Text": "Conversation started via WhatsApp. Conversation link: chatLink"
}
}
]
}
},
{
"redirectLink":{
"request":{
"url":"https://my365398.crm.ondemand.com/sap/public/byd/runtime?bo_ns=http%3A%2F%2Fsap.com%2FthingTypes&bo=COD_GENERIC&node=Root&operation=OnExtInspect&param.Type=COD_CONTACT_TT&param.InternalID=crmContactId"
}
}
}
] ]
} }

View File

@ -187,13 +187,17 @@ const callJournaling = async (req, res) => {
// Remove 0 from the beginning of the number. Ex: 011996067641 to 11996067641 // Remove 0 from the beginning of the number. Ex: 011996067641 to 11996067641
crmPhone = removeZeroInicial(crmPhone) crmPhone = removeZeroInicial(crmPhone)
// test remove // test remove
// return res.status(StatusCodes.OK).send() // return res.status(StatusCodes.OK).send()
// Refactor this in the future. // Refactor this in the future. Integração Gradezco colombia, o crm sap precisa criar o contato com o + no inicio seguido do codigo do pais
crmPhone = '55' + crmPhone if(companyId == "4953"){ // companyId de test para colombia
crmPhone = '+57' + crmPhone
}
else{
crmPhone = '55' + crmPhone
}
console.log('========> CRMPHONE: ', crmPhone) console.log('========> CRMPHONE: ', crmPhone)
console.log('========> COMPANY ID before: ', companyId) console.log('========> COMPANY ID before: ', companyId)
@ -264,6 +268,40 @@ const callJournaling = async (req, res) => {
} }
const contactLink = async (req, res) => {
const { companyId, contactId } = req.query
const crmFiles = await loadCRM(companyId)
const crmContactIds = []
for (const crmConfig of crmFiles) {
const { crmRest: rest, authentication } = crmConfig.crm
// Send the edited contact/lead link url to hitphone to open on another browser tab
let redirectLink = findProperty(rest, 'redirectLink')
if (redirectLink) {
const url = redirectLink?.request?.url?.replace(/crmContactId/g, contactId)
console.log('===============> Contact id redirect Link: ', url)
return res.status(StatusCodes.OK).json({ link: url});
// console.log('new URL(url).hostname: ', new URL(url).hostname)
// crmContactIds.push({ crm: new URL(url).hostname, contactId })
}
//
}
res.status(StatusCodes.NOT_FOUND).json({ message: "Company id not found!"});
}
const sfCreateCase = async (req, res) => { const sfCreateCase = async (req, res) => {
@ -689,7 +727,8 @@ module.exports = {
createTicket, createTicket,
checkContact, checkContact,
contactActivity, contactActivity,
getClientAccessToken getClientAccessToken,
contactLink
} }

View File

@ -1,7 +1,7 @@
const express = require('express') const express = require('express')
const router = express.Router() const router = express.Router()
const { authorization, } = require('../middleware/authentication') const { authorization, } = require('../middleware/authentication')
const { contactCreate, contactActivity, checkContact, sfCreateCase, sfUpdateCase, createTicket, testTemplate, webhook_crm, uploadCrmConfig, callJournaling, oauthCallBack, install, deleteCrm, deleteCompany, getCrms, webhook, getClientAccessToken } = require('../controllers/crmController') const { contactCreate, contactActivity, checkContact, sfCreateCase, sfUpdateCase, createTicket, testTemplate, webhook_crm, uploadCrmConfig, callJournaling, oauthCallBack, install, deleteCrm, deleteCompany, getCrms, webhook, getClientAccessToken, contactLink } = require('../controllers/crmController')
const { fileUpload } = require("../utils") const { fileUpload } = require("../utils")
router.route('/create-contact').post(authorization, contactCreate) router.route('/create-contact').post(authorization, contactCreate)
@ -17,6 +17,9 @@ router.route('/delete-company').post(authorization, deleteCompany)
router.route('/salesforce/case').post(authorization, sfCreateCase) router.route('/salesforce/case').post(authorization, sfCreateCase)
router.route('/salesforce/case').patch(authorization, sfUpdateCase) router.route('/salesforce/case').patch(authorization, sfUpdateCase)
router.route('/oauth-callback').get(oauthCallBack) router.route('/oauth-callback').get(oauthCallBack)
router.route('/contact-link').get(authorization, contactLink)
router.route('/install').get(install) router.route('/install').get(install)
router.route('/test').post(testTemplate) router.route('/test').post(testTemplate)
router.route('/webhook').post(webhook) router.route('/webhook').post(webhook)

View File

@ -8,10 +8,15 @@ const convertToIntegerIfNumber = require('./convertToIntegerIfNumber')
const sendMessageSocket = require('./sendMessageSocket') const sendMessageSocket = require('./sendMessageSocket')
// request, body, crmCallDuration, contact, crmAgent, crmPhone, authentication, rest, companyId // request, body, crmCallDuration, contact, crmAgent, crmPhone, authentication, rest, companyId
async function journalingRequest(request, body, crmCallDuration, contact, crmAgent, crmPhone, authentication, test = {}, companyId = '', dynamicBodyRequest = {}) { async function journalingRequest(request, body, crmCallDuration, contact, crmAgent, crmPhone, authentication, test = {}, companyId = '', dynamicBodyRequest = {}) {
const { requestContentType, requestEncoding, requestType, responseType, url } = request let { requestContentType, requestEncoding, requestType, responseType, url } = request
console.log('----------> crmCallDuration: ', crmCallDuration) console.log('----------> crmCallDuration: ', crmCallDuration)
console.log('----------> url: ', url) console.log('----------> url 1: ', url)
// Second contactId for some cases like SAP crm
if(url.includes("contactObjectId") && contact?.contactId2){
url = url.replace("contactObjectId", contact.contactId2)
}
body = flatten(body) body = flatten(body)

View File

@ -25,13 +25,21 @@ async function lookupContactByPhone(rest, authentication, crmPhone, companyId, t
if (crmInfo) return { exist: true, contactId: crmInfo.contactId, phone: crmPhone } if (crmInfo) return { exist: true, contactId: crmInfo.contactId, phone: crmPhone }
} }
const config = await requestConfigHeader(url, crmPhone, requestType, requestContentType, type, userName, passWord, token, crmClientId, '', '', companyId) let config = await requestConfigHeader(url, crmPhone, requestType, requestContentType, type, userName, passWord, token, crmClientId, '', '', companyId)
if (test?.testing) { if (test?.testing) {
let msg = `Tentanto checar se o contato de numero ${crmPhone} existe no crm` let msg = `Tentanto checar se o contato de numero ${crmPhone} existe no crm`
sendMessageSocket({ companyId, status: 'processing', data: { request: config, msg } }) sendMessageSocket({ companyId, status: 'processing', data: { request: config, msg } })
} }
console.log("PAYLOAD CONFIG LOOKUP CONTACT BY PHONE CONFIG.url 1: ", config.url)
if(config.url.includes("+")){
config.url = config.url.replace("+", "")
}
console.log("PAYLOAD CONFIG LOOKUP CONTACT BY PHONE CONFIG.url 2: ", config.url)
console.log("PAYLOAD CONFIG LOOKUP CONTACT BY PHONE: ", JSON.stringify(config, null, 6)) console.log("PAYLOAD CONFIG LOOKUP CONTACT BY PHONE: ", JSON.stringify(config, null, 6))
let data let data
@ -103,9 +111,10 @@ async function lookupContactByPhone(rest, authentication, crmPhone, companyId, t
let auxContactId let auxContactId
let auxName let auxName
let auxAccountId let auxAccountId
let auxContactId2
for (const prop in data) { for (const prop in data) {
const _prop = prop.replace(/^\d+\./, '').replace(/(?:^|\.)\d+\b/g, '') const _prop = prop.replace(/^\d+\./, '').replace(/(?:^|\.)\d+\b/g, '')
if (_prop == response?.phone?.trim()) { if (_prop == response?.phone?.trim()) {
@ -116,6 +125,10 @@ async function lookupContactByPhone(rest, authentication, crmPhone, companyId, t
auxContactId = data[prop] auxContactId = data[prop]
} }
if (_prop == response?.objectId?.trim()) {
auxContactId2 = data[prop]
}
if (auxPhone && auxContactId) break if (auxPhone && auxContactId) break
} }
@ -128,6 +141,8 @@ async function lookupContactByPhone(rest, authentication, crmPhone, companyId, t
let _prop = prop.replace(/\.(\d+)(\.|$)/g, '[$1]$2') let _prop = prop.replace(/\.(\d+)(\.|$)/g, '[$1]$2')
// console.log("_prop: ", _prop, " | response ", JSON.stringify(response, null, 6))
// SALESFORCE GETTING THE NAME // SALESFORCE GETTING THE NAME
if (_prop == response?.name?.trim()) { if (_prop == response?.name?.trim()) {
auxName = data[prop] auxName = data[prop]
@ -146,6 +161,10 @@ async function lookupContactByPhone(rest, authentication, crmPhone, companyId, t
auxContactId = data[prop] auxContactId = data[prop]
} }
if (_prop == response?.objectId?.trim()) {
auxContactId2 = data[prop]
}
// SALESFORCE CASE LOOOK UP ALL THE OBJECT PROPERTIES // SALESFORCE CASE LOOOK UP ALL THE OBJECT PROPERTIES
if (!url.includes('salesforce')) if (!url.includes('salesforce'))
if (auxPhone && auxContactId) break if (auxPhone && auxContactId) break
@ -160,7 +179,7 @@ async function lookupContactByPhone(rest, authentication, crmPhone, companyId, t
auxAccountId = crmAccountId auxAccountId = crmAccountId
} }
console.log('---------> auxPhone: ', auxPhone, ' | auxContactId: ', auxContactId, ' | auxAccountId: ', auxAccountId) console.log('---------> auxPhone: ', auxPhone, ' | auxContactId: ', auxContactId, ' | auxAccountId: ', auxAccountId, ' | auxContactId2: ', auxContactId2)
if (auxPhone) { if (auxPhone) {
@ -202,7 +221,7 @@ async function lookupContactByPhone(rest, authentication, crmPhone, companyId, t
} }
return { exist: true, contactId: auxContactId, phone: crmPhone, name: auxName, accountId: auxAccountId } return { exist: true, contactId: auxContactId, phone: crmPhone, name: auxName, accountId: auxAccountId, contactId2: auxContactId2}
} }
return { exist: false } return { exist: false }

View File

@ -11,7 +11,7 @@ async function templateValidator(crmPhoneTest, crm, companyId) {
let contact = await lookupContactByPhone( let contact = await lookupContactByPhone(
rest = crm.crmRest, rest = crm.crmRest,
authentication = crm.authentication, authentication = crm.authentication,
crmPhone = phoneTest, crmPhone = phoneTest.replace("+", ""),
companyId, companyId,
test = { testing: true }) test = { testing: true })

View File

@ -37,6 +37,8 @@ async function whatsappJournalingCRM(companyId, crmPhone = '', crmAgent, crmFirs
let contact = await _lookupContact(rest, authentication, crmPhone, crmEmail, companyId, crmFirstName) let contact = await _lookupContact(rest, authentication, crmPhone, crmEmail, companyId, crmFirstName)
console.log("================> whatsappJournalingCRM contact: ", JSON.stringify(contact, null, 6))
let { request, chats, response } = chatJournaling let { request, chats, response } = chatJournaling
let body = findProperty(chats, 'chatDone') let body = findProperty(chats, 'chatDone')
@ -128,7 +130,7 @@ async function _lookupContact(rest, authentication, crmPhone, crmEmail, companyI
// 3. Se o contato já existe (seja por telefone ou e-mail), retorna as informações // 3. Se o contato já existe (seja por telefone ou e-mail), retorna as informações
if (contact?.exist) { if (contact?.exist) {
console.log(`Contato encontrado! ID: ${contact.contactId}`); console.log(`Contato encontrado! ID: ${contact.contactId}`);
return { created: false, contactId: contact.contactId }; return { created: false, contactId: contact.contactId, contactId2: contact?.contactId2 };
} }
// 4. Se o contato não foi encontrado por nenhuma das formas, cria um novo // 4. Se o contato não foi encontrado por nenhuma das formas, cria um novo

View File

@ -1,13 +1,13 @@
{ {
"files": { "files": {
"main.css": "/static/css/main.ae60ab08.css", "main.css": "/static/css/main.ae60ab08.css",
"main.js": "/static/js/main.b7e32f90.js", "main.js": "/static/js/main.e3056144.js",
"index.html": "/index.html", "index.html": "/index.html",
"main.ae60ab08.css.map": "/static/css/main.ae60ab08.css.map", "main.ae60ab08.css.map": "/static/css/main.ae60ab08.css.map",
"main.b7e32f90.js.map": "/static/js/main.b7e32f90.js.map" "main.e3056144.js.map": "/static/js/main.e3056144.js.map"
}, },
"entrypoints": [ "entrypoints": [
"static/css/main.ae60ab08.css", "static/css/main.ae60ab08.css",
"static/js/main.b7e32f90.js" "static/js/main.e3056144.js"
] ]
} }

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>React App</title><script defer="defer" src="/static/js/main.b7e32f90.js"></script><link href="/static/css/main.ae60ab08.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html> <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>React App</title><script defer="defer" src="/static/js/main.e3056144.js"></script><link href="/static/css/main.ae60ab08.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

View File

@ -1,61 +0,0 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/*!
Copyright (c) 2015 Jed Watson.
Based on code that is Copyright 2013-2015, Facebook, Inc.
All rights reserved.
*/
/*!
* Adapted from jQuery UI core
*
* http://jqueryui.com
*
* Copyright 2014 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/category/ui-core/
*/
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/** @license React v0.20.2
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

File diff suppressed because one or more lines are too long