I have a custom object in Salesforce which I need to setup a Master Detail relationship from Accounts. Accounts being the Master and CompHist being the Detail. The problem I am running into is that I need to set the relation to work off of custom fields within the objects. Example:
1.) Accounts has a custom field called CustomerId.
2.) CompHist also has custom field called CustomerId.
3.) I need to be able to have this linked together by CustomerId field for report generation.
About 2,000 records are inserted into CompHist around the 8th of each month. This is done from a .NET application that kicks off at the scheduled time, collects info from our databases and then uploads that data to salesforce via the SOAP API.
Maybe I'm misunderstanding how Salesforce relationships work as I am fairly new (couple months) to salesforce development.
Thanks,
Randy
There is a way to get this to work without triggers that will link the records or pre-querying the SF to learn Account Ids in .NET before you'll push the CompHistories.
Setup
On Account: set the "External ID" checkbox on your CustomerId field. I'd recommend setting "Unique" too.
On CompHist: you'll need to make decision whether it's acceptable to move them around or when the relation to Account is set - it'll stay like that forever. When you've made that decision tick / untick the "reparentable master-detail" in the definition of your lookup / m-d to Account.
And if you have some Id on these details, something like "line item number" - consider making an Ext. Id. for them too. Might save your bacon some time in future when end user questions the report or you'll have to make some kind of "flush" and push all lines from .NET (will help you figure out what's to insert, what's to update).
At this point it's useful to think how are you going to fill the missing data (all the nulls in the Ext. Id) field.
Actual establishing of the relationship
If you have the external ids set it's pretty easy to tell salesforce to figure out the linking for you. The operation is called upsert (mix between update and insert) and can be used in 2 flavours.
"Basic" upsert is for create/update solving; means "dear Salesforce, please save this CompHist record with MyId=1234. I don't know what's the Id in your database and frankly I don't care, go figure this out will ya?"
If there was no such record - 1 will be created.
If there was exactly 1 match - it will be updated.
If there were more than 1 found - SF won't know which one to update and throw error back at you (that's why marking as "unique" is a good idea. There's a chance you'll spot errors sooner).
"Advanced" upsert is for maintaining foreign keys, establishing lookups. "Dear SF, please hook this CompHist up to Account which is marked as "ABZ123" in my DB. Did I mention I don't care about your Ids and I can't be bothered to query your database first prior to me uploading my stuff?"
Again - exact match - works as expected.
0 or 2 Accounts with same ext. id value = error.
Code plz
I'd recommend you to play with Data Loader or similar tool first to get a grasp. of what exactly happens, how to map fields and how to not be confused (these 2 flavours of upsert can be used at same time). Once you'll manage to push the changes the way you want you can modify your integration a bit.
SOAP API upsert: http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_upsert.htm (C# example at the bottom)
REST API: http://www.salesforce.com/us/developer/docs/api_rest/Content/dome_upsert.htm
If you'd prefer an Salesforce Apex example: Can I insert deserialized JSON SObjects from another Salesforce org into my org?
Related
Hope you're well. I'm currently building out a report, but despite my best efforts so far, I can't get some information to populate within the report. It does not appear to me that salesforce is recognizing the field "Agent Incoming Connecting Time" within the object "AC_Agent_Performance". However, I can pull in some other fields within the same object into the Agent Performance report, so I'm not clear on what is not taking place in the field that I wish to see within the report. Here are some of the things that I've tried:
I have checked the access to the field. The first photo (Photo 1) Shows an example of a working object, the the second one shows an example of one that does not.
The API name seems to work, and is consistent with other fields within the object that work.
I have checked the page layout for the object (even though I don't think this is the issue), and I have mirrored other fields to the best of my knowledge that ARE populating within the report.
I reviewed the CTI flows to see if there was something missing in there on a lark, but there was nothing in there that would lead me to believe that this was the source of the problem.
I have tried setting up a new field in the object (formula), that references the field that I'm trying. to pull in, but that just returns a result of 'zero' for all values.
One thing that I have done that appears to be working, is I have set up a joined report, which uses both "AC Agent Performance" object and "AC Historical Queue Metrics" object in the report. The result that is returning appears to be accurate (please see the picture (picture number 3)). However, I don't think that this is the right way to go about this, and I don't want to do it this way. I want to use the report with one object rather than with two.
I know that permissions are the most likely issue, so I've taken a close look at these. Please let me know if there is something wrong with how I have the permissions configured. The First image depicts the 'Field Level Security'. The second image depicts the'field accessibility'. They are both like this, the whole way down:
Please note one other thing, which is that the last picture depicts a different field within the object displaying in the report.
Does anyone have any ideas on how I can proceed so the field "Agent Incoming Connecting Time" will display within the report?
Please also note, that these are objects that contain data that is populated from AWS' Amazon Connect.
This last photo, shows that the object does not have any information in it within the report.
If the field isn't populated there's not much you can do on the reporting side of things. You already tried "joined report". You should check why the integration doesn't populate it, maybe read integration documentation, contact the managed package's support...
The tables are connected with lookup or master-detail, right? In a pinch you could try making formula field on "AC Agent Performance" looking "up" and pulling the value from related AC historical queue metrics. If the relationship is other way around (performance -> down to related list -> metrics) you could try to make-do with a master detail and rollup summary field. I don't know this package, no idea if you can pull it off when you don't have full control over the fields.
If you can't really use the relationships and absolutely need to report on single table - you could capture intermediate results of the report to a helper table and then report on that. It's called "reporting snapshots". Or write some nightly (hourly?) batch that recalculates stuff and writes homemade "rollup" to these fields?
I'm a little stuck.
I am trying to generate a report that determines whether anyone has made a manual change to certain fields within our order framework. I have figured out the proper fields and structures to audit, and even how to make the report, but I used a combination of extracts from the Dataloader and Excel xlookups to make it. Now, I'm being asked to find a way to automate the generation of the report, and I suspect that means I need to write a SOQL query to figure it out. I'm having trouble traversing multiple relationships based on these ID fields. Essentially, what I'm trying to do is make multiple "left joins" based on the 18 digit Salesforce IDs and extract some related piece of information from those other objects.
For example, if I'm starting with order_product_history (with a field OrderProductID to identify the order product) and I want to bring in "Product Name", I have to first match OrderProductID with the ID field in my order_product "table", then I have to match the Product2ID field in my order_product "table" with the ID in my product "table", then I have to get the matching Product Name as a column in my report:
Matching/Traversal Process
Desired Result
That's one example for one field. I also have to bring in things like User Name from the users "table", and order number from the orders table, but once I get the general idea, I think I'll be OK. I also want to filter the results to only include my Fee__c and UnitPrice fields, ignore the automated users and set a date filter--not sure if I have to do that using a WHERE clause just in my main query, or if I have to filter the subqueries as well.
I am not a programmer and I have no formal Salesforce training; I am just an analyst who is technically inclined and sort of fell into the role of Salesforce Admin. I am familiar with programming concepts and have been writing things using the flow application and have even dipped my toes into some Apex stuff, but it can be a bit of a struggle. I am not asking you to do my job for me and I am willing to work at the problem and learn; any help is appreciated. Sorry about the links; SO won't let me embed images yet.
There's high chance you don't have to write any code for it. I'll give you some tips, experiment a bit and edit the question or post new one?
This diagram might help: https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_erd_products.htm
Developer way
It's all about writing the right query. You can play with it in Developer Console or Workbench for example. Read up about relationship queries in SF.
https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_relationships_understanding.htm
https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_relationships_query_hist.htm
I don't have org with orders enabled but this should be a good start:
SELECT CreatedById, Created.Name,
Parent.Name, Parent.Product2.Name, Parent.Order.Name,
Field, OldValue, NewValue, CreatedDate
FROM OrderItemHistory
If it throws errors about "Parent" see if "OrderItem" will work. Once you have it - WHERE Field IN ('UnitPrice', 'Fee__c') AND CreatedDate = LAST_WEEK might be good next step. (dates can be filtered by date but there are few "constants" that are more human-readable too)
You could even export this with Data Loader, you just have to start the wizard on Order Product history table. But you can ignore the whole query creator and paste a query you've created.
Admin way
Have you ever created a report in Salesforce? There are self-paced trainings (for Lightning and Classic UI) and tons of YouTube videos. Get a feel of making few reports.
What you want might be doable with built-in report type (see if there's new report -> order product history). If nothing exciting - as admin you can make new report type in setup. For example Orders -> Order Products -> Order Product History. Screenshots from here might help.
Just wanted to update this for anyone looking for a solution in the future.
Turns out you can traverse these as a parent-child relationship in the SOQL query. Here's the query I ended up using:
SELECT CreatedBy.Name, FORMAT(CreatedDate), OrderItem.Order.OrderNumber, OrderItem.Product2.Name, OrderItem.Product2.ProductCode, Field, OldValue, NewValue
FROM OrderItemHistory
WHERE (Field = 'Fee__c' OR UnitPrice) AND (CreatedBy.Name != 'Integration User') AND (Created Date >= 2020-11-24T00:00:00.000Z) ORDER BY CreatedDate DESC
I want to display all audit history data as per MS CRM format.
I have imported all records from AuditBase table from CRM to another Database server table.
I want this table records using SQL query in Dynamics CRM format (as per above image).
I have done so far
select
AB.CreatedOn as [Created On],SUB.FullName [Changed By],
Value as Event,ab.AttributeMask [Changed Field],
AB.changeData [Old Value],'' [New Value] from Auditbase AB
inner join StringMap SM on SM.AttributeValue=AB.Action and SM.AttributeName='action'
inner join SystemUserBase SUB on SUB.SystemUserId=AB.UserId
--inner join MetadataSchema.Attribute ar on ab.AttributeMask = ar.ColumnNumber
--INNER JOIN MetadataSchema.Entity en ON ar.EntityId = en.EntityId and en.ObjectTypeCode=AB.ObjectTypeCode
--inner join Contact C on C.ContactId=AB.ObjectId
where objectid='00000000-0000-0000-000-000000000000'
Order by AB.CreatedOn desc
My problem is AttributeMask is a comma separated value that i need to compare with MetadataSchema.Attribute table's columnnumber field. And how to get New value from that entity.
I have already checked this link : Sql query to get data from audit history for opportunity entity, but its not giving me the [New Value].
NOTE : I can not use "RetrieveRecordChangeHistoryResponse", because i need to show these data in external webpage from sql table(Not CRM database).
Well, basically Dynamics CRM does not create this Audit View (the way you see it in CRM) using SQL query, so if you succeed in doing it, Microsoft will probably buy it from you as it would be much faster than the way it's currently handled :)
But really - the way it works currently, SQL is used only for obtaining all relevant Audit view records (without any matching with attributes metadata or whatever) and then, all the parsing and matching with metadata is done in .NET application. The logic is quite complex and there are so many different cases to handle, that I believe that recreating this in SQL would require not just some simple "select" query, but in fact some really complex procedure (and still that might be not enough, because not everything in CRM is kept in database, some things are simply compiled into the libraries of application) and weeks or maybe even months for one person to accomplish (of course that's my opinion, maybe some T-SQL guru will prove me wrong).
So, I would do it differently - use RetrieveRecordChangeHistoryRequest (which was already mentioned in some answers) to get all the Audit Details (already parsed and ready to use) using some kind of .NET application (probably running periodically, or maybe triggered by a plugin in CRM etc.) and put them in some Database in user-friendly format. You can then consume this database with whatever external application you want.
Also I don't understand your comment:
I can not use "RetrieveRecordChangeHistoryResponse", because i need to
show these data in external webpage from sql table(Not CRM database)
What kind of application cannot call external service (you can create a custom service, don't have to use CRM service) to get some data, but can access external database? You should not read from the db directly, better approach would be to prepare a web service returning the audit you want (using CRM SDK under the hood) and calling this service by external application. Unless of course your external app is only capable of reading databases, not running any custom web services...
It is not possible to reconstruct a complete audit history from the AuditBase tables alone. For the current values you still need the tables that are being audited.
The queries you would need to construct are complex and writing them may be avoided in case the RetrieveRecordChangeHistoryRequest is a suitable option as well.
(See also How to get audit record details using FetchXML on SO.)
NOTE
This answer was submitted before the original question was extended stating that the RetrieveRecordChangeHistoryRequest cannot be used.
As I said in comments, Audit table will have old value & new value, but not current value. Current value will be pushed as new value when next update happens.
In your OP query, ab.AttributeMask will return comma "," separated values and AB.changeData will return tilde "~" separated values. Read more
I assume you are fine with "~" separated values as Old Value column, want to show current values of fields in New Value column. This is not going to work when multiple fields are enabled for audit. You have to split the Attribute mask field value into CRM fields from AttributeView using ColumnNumber & get the required result.
I would recommend the below reference blog to start with, once you get the expected result, you can pull the current field value using extra query either in SQL or using C# in front end. But you should concatenate again with "~" for values to maintain the format.
https://marcuscrast.wordpress.com/2012/01/14/dynamics-crm-2011-audit-report-in-ssrs/
Update:
From the above blog, you can tweak the SP query with your fields, then convert the last select statement to 'select into' to create a new table for your storage.
Modify the Stored procedure to fetch the delta based on last run. Configure the sql job & schedule to run every day or so, to populate the table.
Then select & display the data as the way you want. I did the same in PowerBI under 3 days.
Pros/Cons: Obviously this requirement is for reporting purpose. Globally reporting requirements will be mirroring database by replication or other means and won't be interrupting Prod users & Async server by injecting plugins or any On demand Adhoc service calls. Moreover you have access to database & not CRM online. Better not to reinvent the wheel & take forward the available solution. This is my humble opinion & based on a Microsoft internal project implementation.
BACK END - SQL Server
FRONT END - Access 2010 (2000 format)
The system stores and retrieves data about technical documents. Broadly, there are three tables A, B and C, each of which maintains data about a different type of document.
The ACCESS front end provides a Search Form and Data Entry/Edit form (bound to the underlying table) for each document type. In all three document types, when adding a new record, the user will open the Search form and press a button called "Add". This opens the Data Entry/Edit form and in the Form_Load event is the line
DoCmd.GoToRecord , , acNewRec
When the data entry is complete, the user presses a "Close" button which simply runs the code
DoCmd.Close
As I said, the design and code of the objects relating to the three document types is, for all intents and purposes, identical. However, while for tables A and B the process of adding a new record is seamless and extremely quick, for table C it has proved impossible to add a new record via the ACCESS UI. The edit form will open correctly to add the data, but when the user presses the "Close" button the form hangs, and eventually returns to the Search form without the new record having been added.
It is possible to bypass the UI by opening ACCESS while holding down the SHIFT key, opening the linked table, and adding new records directly. While this is acceptable as an interim measure, it is unacceptable in the long term. It should be noted that the system is about ten years old, and has been working entirely correctly for about nine of those years (apart from minor glitches moving between different versions of ACCESS).
Unfortunately this system is owned and operated by a major global corporation and it is very difficult for me, a subcontracted supplier, to get access to the SQL Server box to run diagnosis (SQL Profiler would be a good starting point). My gut feeling is that there is a subtle difference in the permissions model for that particular table but I don't know.
The situation is further complicated by the fact that I have a copy of the system at my work and I cannot reproduce the problem. Of course, there are bound to be subtle differences between the two architectures (for example, I don't know for certain what version of SQL Server it's running on, but I believe it's 2000, nor do I know how completely it is patched or updated) but the facts are that for one particular table bound to one particular form, it is not possible to add records, whereas for other tables there is no such problem.
I would be grateful if anyone has any ideas about how to go about diagnosing this or even solving it (if anyone has come across the same problem before).
Many thanks
Edward
As a general rule when you encounter problems to update a table, then this tends to suggests that the table does not have a PK or the form the query is based on does not have a PK exposed.
The next thing I would ensure is the table has a time stamp column as Access uses this to test for record changes behind the scenes.
Next up I would check the default locking for the form (while these settings generally don't effect odbc, they should be checked).
Next up is to check if the table has any "bit" column (true/false) and ensure that the defaults for such controls are set SQL SERVER side (they should default to 0). This null bits issue will cause updates to fail if not addressed.
I would also check if the form in question is based on a query or if the data source is set directly to the table. As noted the PK auto number ID of that table in query should be INTEGER value sql side – big int is NOT supported.
So check default values (both in sql table and on the form (those controls) to ensure nothing be set that would prevent the update.
I haven't been able to find any information regarding the best way to handle record editing with approval in CakePHP.
Specifically, I need to allow users to edit data in a record, but the edited data should not overwrite the original record data until administrators have approved the change. I could put the edited records in a new table and then overwrite the originals when I approve them but I wonder if there is an easier way since this idea doesn't seem to play well with the cake philosophy so to speak.
You are going to need somewhere to store that data until an administrator can approve it.
I'm not sure how this can be easier than creating another table with the new edits and the original post id. Then when an administrator approves the edit, the script overwrites the old record with the edited version.
I'm working on a similar setup and I'm going with storing the draft record in the same table but with a flag set on the record called "draft". Also, the original record has a "draft_id" field that has the id of the draft record stored in it.
Then in the model when the original record is loaded by the display engine it shows it normally. But when the edit or preview actions try to load the record, it checks the "draft_id" field and then loads the other record if it's set.
The "draft" flag is used to keep list and other group find type actions from grabbing the draft records too. This might also be solved by a more advanced SQL query but I'm not quite that good with SQL.