CreateContactRecord cannot access variables from Matching Scenario

Status
Not open for further replies.

grayedfox

Forum User
Joined
Apr 17, 2019
Messages
16
Reaction score
0
Hi,

I've nearly finished our Copper <> 3CX integration however cannot get the CreateContactRecord scenario to work. The problem is that the CreateContactRecord scenario should only be triggered if the lookup fails in the matching scenario, however I cannot figure out how to skip the CreateContactRecord based on the output of Matching Scenario.

When the match returns a contact (it uses a POST instead of a get request), unfortunately the CreateContactRecord always triggers.

I could fix this if the CreateContactRecord had access to the variables defined inside the Matching Scenario, but it does not.

Here is the relevant part of the XML:

<?xml version="1.0"?>
<Crm xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Country="DE" Name="Copper3CXv2" Version="2">
<Number Prefix="AsIs" MaxLength="14" />
<Connection MaxConcurrentRequests="8" />
<Parameters>
<Parameter Name="X-PW-AccessToken" Type="String" Editor="String" Title="Copper API Key:" Default="" ListValues="" />
<Parameter Name="X-PW-Application" Type="String" Parent="" Editor="String" Title="Copper Application:" Default="developer_api" ListValues="" />
<Parameter Name="X-PW-UserEmail" Type="String" Editor="String" Title="Copper Dev Email:" Default="" ListValues="" />
<Parameter Name="CopperAPI" Type="String" Editor="String" Title="Copper API URL:" Default="https://api.prosperworks.com/developer_api/v1" ListValues="" />
<Parameter Name="PhoneCallActivityId" Type="String" Editor="String" Title="Copper Activity ID:" Default="781151" />
<Parameter Name="InboundCallText" Type="String" Editor="String" Title="Answered Inbound Call:" Default="[DateTime]: [Agent] answered call from [Contact::FirstName] [Contact::LastName] ([Number]) ([Duration])" />
<Parameter Name="MissedInboundCallText" Type="String" Editor="String" Title="Missed Inbound Call:" Default="[DateTime]: [Agent] missed call from [Contact::FirstName] [Contact::LastName] ([Number])" />
<Parameter Name="OutboundCallText" Type="String" Editor="String" Title="Answered Outbound Call:" Default="[DateTime]: [Agent] called [Contact::FirstName] [Contact::LastName] ([Number]) ([Duration])" />
<Parameter Name="MissedOutboundCallText" Type="String" Editor="String" Title="Missed Outbound Call:" Default="[DateTime]: [Agent] attempted to call [Contact::FirstName] [Contact::LastName] ([Number])" />
<Parameter Name="ReportCallEnabled" Type="Boolean" Editor="String" Title="Enable Call Journaling" Default="True" />
<Parameter Name="CreateContactEnabled" Type="Boolean" Editor="String" Title="Enable Contact Creation:" Default="False" />
<Parameter Name="CreateContactName" Type="Double" Editor="String" Title="Default Contact Name:" Default="3CXContact" ListValues="3CXContact,NewContact" />
</Parameters>
<Authentication Type="No">
<Value />
</Authentication>
<Scenarios>
<Scenario Id="" Type="REST">
<Request SkipIf="" Url="[CopperAPI]/people/search" Message="" RequestContentType="application/json" RequestEncoding="Json" RequestType="Post" ResponseType="Json">
<Headers>
<Value Key="X-PW-AccessToken" Passes="0" Type="String">[X-PW-AccessToken]</Value>
<Value Key="X-PW-Application" Passes="0" Type="String">[X-PW-Application]</Value>
<Value Key="X-PW-UserEmail" Passes="0" Type="String">[X-PW-UserEmail]</Value>
</Headers>
<PostValues>
<Value Key="sort_by" Passes="0" Type="String">date_modified</Value>
<Value Key="sort_direction" Passes="0" Type="String">desc</Value>
<Value Key="page_size" Passes="0" Type="String">1</Value>
<Value Key="phone_number" Passes="0" Type="String">[Number]</Value>
</PostValues>
</Request>
<Rules>
<Rule Type="Number" Ethalon="">phone_numbers.number</Rule>
</Rules>
<Variables>
<Variable Name="CopperID" Path="id">
<Filter />
</Variable>
<Variable Name="FirstName" Path="first_name">
<Filter />
</Variable>
<Variable Name="LastName" Path="last_name">
<Filter />
</Variable>
<Variable Name="CompanyName" Path="company_name">
<Filter />
</Variable>
<Variable Name="WorkPhone" Path="phone_numbers.number">
<Filter>
<Rule Type="Equals" Ethalon="work">phone_numbers.category</Rule>
</Filter>
</Variable>
<Variable Name="MobilePhone" Path="phone_numbers.number">
<Filter>
<Rule Type="Equals" Ethalon="mobile">phone_numbers.category</Rule>
</Filter>
</Variable>
<Variable Name="HomePhone" Path="phone_numbers.number">
<Filter>
<Rule Type="Equals" Ethalon="home">phone_numbers.category</Rule>
</Filter>
</Variable>
<Variable Name="PersonalEmail" Path="emails.email">
<Filter>
<Rule Type="Equals" Ethalon="personal">emails.category</Rule>
</Filter>
</Variable>
<Variable Name="WorkEmail" Path="emails.email">
<Filter>
<Rule Type="Equals" Ethalon="work">emails.category</Rule>
</Filter>
</Variable>
</Variables>
<Outputs Next="" AllowEmpty="false">
<Output Type="CopperID" Passes="0" Value="[CopperID]" />
<Output Type="FirstName" Passes="0" Value="[FirstName]" />
<Output Type="LastName" Passes="0" Value="[LastName]" />
<Output Type="CompanyName" Passes="0" Value="company_name" />
<Output Type="PhoneBusiness" Passes="0" Value="[WorkPhone]" />
<Output Type="PhoneMobile" Passes="0" Value="[MobilePhone]" />
<Output Type="PhoneHome" Passes="0" Value="[HomePhone]" />
<Output Type="EmailPersonal" Passes="0" Value="[PersonalEmail]" />
<Output Type="EmailWork" Passes="0" Value="[WorkEmail]" />
</Outputs>
</Scenario>
<Scenario Id="CreateContactRecord" Type="REST">
<Request SkipIf="[CreateContactEnabled]!=True||[CopperID]!=null" Url="[CopperAPI]/people" Message="" RequestContentType="application/json" RequestEncoding="Json" RequestType="Post" ResponseType="Json">
<Headers>
<Value Key="X-PW-AccessToken" Passes="0" Type="String">[X-PW-AccessToken]</Value>
<Value Key="X-PW-Application" Passes="0" Type="String">[X-PW-Application]</Value>
<Value Key="X-PW-UserEmail" Passes="0" Type="String">[X-PW-UserEmail]</Value>
</Headers>
<PostValues>
<Value Key="name" Passes="0" Type="String">[CreateContactName]</Value>
<Value Key="details" Passes="0" Type="String">Created by 3CX</Value>
<Value Key="first_name" Passes="0" Type="String">[CreateContactName]</Value>
<Array Key="phone_numbers">
<Object>
<Value Key="number" Passes="0" Type="String">[Number]</Value>
<Value Key="category" Passes="0" Type="String">mobile</Value>
</Object>
</Array>
</PostValues>
</Request>
<Variables />
<Outputs AllowEmpty="false" />
</Scenario>
</Scenarios>
</Crm>


I would expect to be able to access the variables from the Matching Scenario – but my problem would be solved if I could somehow prevent the CreateContactRecord scenario from triggering every time (even when a matching contact is found in the Matching Scenario).
 
I tried making a custom create contact callback which I put as the Next scenario after the match but this breaks call journaling :(
 
To clarify: the bug is that when using the recommended reserved scenarios, the CreateContactRecord scenario always triggers even when the Matching Scenario finds a matching contact. I think it may have something to do with the fact that the request fired during the contact lookup is a POST and not a GET – although I have set it up in such a way that the contact lookup only ever returns a single result (despite having multiple matches).
 
You're doing something wrong here.

The CreateContactRecord scenario is ONLY triggered when the matching scenario DOESN'T return any record. And you can't access any output from the matching scenario, because the matching scenario didn't return data...

Maybe your matching scenario is retruning data, but not having the phone number, and that causes the problem. The CRM engine analyzes if the returned data matches the caller phone number, and discards any record not having the phone number. So, even if you're returning data, it might be discarded by the CRM engine, and then the CreateContactRecord scenario is invoked.
 
Hello @edossantos thank you for your reply. I have attached two screen shots to prove that the lookup returns a match and fires the CreateContactRecord scenario anyway:

1. match a record
10356

2. create contact despite match returned
10357

Other details: I followed the guide here: https://www.3cx.com/docs/crm-integration/#h.fr831io045wv

The guide is a bit out of date but I used the XML reference too: https://www.3cx.com/docs/server-side-crm-template-xml-description/#h.vcivldjsa1lz

Added info:
  • I have exactly 3 scenarios (as you can see in the screenshots)
  • I only use the reserved scenarios (there are no custom scenarios)
  • I do not do any request chaining (none of the scenarios explicitly call one another)
  • the Matching Scenario sends a POST (not a GET) request to Copper and only ever returns a single result (limit page_size to 1)
  • the ReportCall scenario only works if the CreateContactRecord scenario is completely removed (not only skipped using the boolean value, but deleted)
  • changing the order of the report call and create contact scenarios (moving up and down) does nothing
I think this is a genuine bug, not some sort of configuration error. Would be great to get to the bottom of it, or at least figure out a workaround for now (i.e. is there a way to avoid using the CRM engines reserved scenarios and achieve the same result?)

CopperAPI dev docs link for convenience: https://developer.copper.com/
 
I think I figured it out. If testing the behaviour of the integration with a number 6 digits long (i.e. the default 123456) it somehow breaks the CRM logic.

Using a longer number (i.e. +4917611223344) triggered the lookup, create contact, and call logging scenarios as expected. So, I guess the bug is "CRM scenario logic breaks for numbers with small length"
 
I don't think the problem is with short numbers. We have tested the engine with short and long numbers without any issue.

The problem is probably that the returned contact doesn't have the phone number 123456, or is not matched to an output. What contact are you returning in that case? Can you show how the response is matched to variables (the Response Tree and Variables tab from the tool)?
 
To be honest the screen shot shows that the number used in the look up is 123456 – which is found and matched to a contact. In the response the JSON structure is as follows:

[{ "id": number, "other": "values", "phone_numbers": [{ "category": "mobile", "number": 123456 }] }]`

Perhaps the 3CX engine doesn't like nested values? The response is normally an array of objects, and each object has a "phone_numbers" object that can contain multiple values. Unless 3CX does some weird things with pagination when it knows there are more pages... I think that part is all fine really.

So, I just tripple checked: it is 100% the length of the number. I tried changing the parameter "MaxCompareLength" to 6 and everything works as expected. If I leave it at the default value of 7, and also leave the default value of contact lookup as 123456, then the match happens however the CRM template tool shows that the Variable and Output tabs are empty. The match is followed by the createContactRecord scenario.

maxCompareLength set to 6 (works)
10363

maxCompareLength set to 7 (default, doesn't work)
10365

I searched the XML template docs and cannot find a reference to that variable anywhere - I guess it's only used to help with testing - and assume it thus has no bearing on the generated xml file behaviour (there is no reference to it in the generated file either, searched using a text editor).

Would be nice if the default value inside the template tool was set to 7 so that using the default parameters (i.e. the number 123456 for performing contact matching etc) actually works out of the box - lost a heap of time to this!

Thanks for the support.
 
The problem is that you're setting MaxCompareLength to 7 and the number length is 6. That causes that there is no match (you will not see any row in the Output tab), and then the CreateContactRecord scenario is triggered.

This is the expected behaviour for this case. If you need to deal with short numbers, then reduce the value of MaxCompareLength, or configure the 3CX Console > Advanced > Contacts > Options > Match Caller ID's to a contact entry > Match exactly.

Please note that the CRM engine only works with calls coming from external lines, so you will never receive such a short number in a real environment.
 
Good morning @edossantos!

This might be expected behaviour given these settings but this was not at all obvious for me, as a first time user of the XML generator. I would really appreciate it if you could pass on some feedback to the product owner/maintainers of the XML template tool:

  • rename MaxCompareLength to MinCompareLength (which seems to be what it really is: if the number is less than the MinCompareLength, no comparison is made)
  • document the MinCompareLength setting somewhere to explain what it does and how it effects the template tool
  • change the default MinCompareLength value to 6 (or change the default contact lookup and report call numbers to 012345678, or both)

As you said yourself: in the real world we will never receive such a short number. It thus makes sense to use default settings that reflect real world data, and not have the default settings of the XML generator be set to data that has strange outcomes (like getting a match but then then firing the lookup scenario anyway).
 
Thanks for your feedback @grayedfox! I will pass this to the product manager. However:

rename MaxCompareLength to MinCompareLength (which seems to be what it really is: if the number is less than the MinCompareLength, no comparison is made)
Actually, this setting controls the max compare length. If you set it to 7 and the number is 1234567890, the comparisson will be made against 4567890 (the last 7 digits).

As you said yourself: in the real world we will never receive such a short number. It thus makes sense to use default settings that reflect real world data, and not have the default settings of the XML generator be set to data that has strange outcomes (like getting a match but then then firing the lookup scenario anyway).
I agree with you, the default value for the tool should be larger, for example 10 digits, as that's the most common case in real environments.
 
  • Like
Reactions: grayedfox
Hi @edossantos ! Happy to give feedback, no worries. In terms of the naming/logic of the variable: if the max compare length is 10 and the number is only 9 digits long, I would still expect the 9 digit number (the entire number) to be compared. I.e: 123456789 should use all digits for the comparison instead of making no comparison.

I think somewhere inside the CRM template tool, a conditional check is being made that skips the comparison entirely if the number isn't greater than or equal to maxCompareLength, which I think is a mistake.

This means numbers have to be at least N digits long to be compared, which means we are breaking the single responsibility principle and using the maxCompareLength variable for two different purposes:
  1. for checking whether or not to make a comparison based on min length, and
  2. for discarding extra digits we do not want to use in the comparison
It might make sense to either introduce and expose a new variable (i.e. minCompareThreshold), or to remove that conditional check, so that numbers used that are smaller than maxCompareLength but greater than minCompareThreshold still behave intuitively.
 
In terms of the naming/logic of the variable: if the max compare length is 10 and the number is only 9 digits long, I would still expect the 9 digit number (the entire number) to be compared. I.e: 123456789 should use all digits for the comparison instead of making no comparison.

I think somewhere inside the CRM template tool, a conditional check is being made that skips the comparison entirely if the number isn't greater than or equal to maxCompareLength, which I think is a mistake.

This means numbers have to be at least N digits long to be compared, which means we are breaking the single responsibility principle and using the maxCompareLength variable for two different purposes:
  1. for checking whether or not to make a comparison based on min length, and
  2. for discarding extra digits we do not want to use in the comparison
It might make sense to either introduce and expose a new variable (i.e. minCompareThreshold), or to remove that conditional check, so that numbers used that are smaller than maxCompareLength but greater than minCompareThreshold still behave intuitively.
Yes, this makes sense, I think there is room for improvement here. I will report this internally.
 
  • Like
Reactions: grayedfox
Status
Not open for further replies.

Latest Posts

Forum statistics

Threads
112,324
Messages
591,897
Members
165,475
Latest member
lastshadow