I was able to add features as children to initiatives by updating the parent field of the feature to point to the initiative (add features to initiative as children (php api))
I assumed this would be the same way to do add user stories as children to features -- by updating the field of the user story to point to its feature.
This is the code that's giving me errors:
$rally->update("story", $story['ObjectID'], array('Parent' => $feature['ObjectID']));
This is the error I'm getting:
PHP Fatal error: Uncaught exception 'RallyApiError' with message 'Could not read: Could not read referenced object 13317970914'
It seems like it's not letting me reference the feature ID..why is this happening?
You may update PortfolioItem field on a story.
In WS API doc Parent field on a Hierarchical Requirement(a.k.a. story) is expected to be another Hierarchical Requirement. It cannot be a Feature.
Also, Feature field on a story is read-only, and UserStories collection on Feature is read-only. That leaves us with the option to update PortfolioItem field on a story.
Here is a Ruby code that illustrates it:
#rally = RallyAPI::RallyRestJson.new(config)
obj = {}
obj["Name"] = "new story efd"
new_s = #rally.create("hierarchicalrequirement", obj)
query = RallyAPI::RallyQuery.new()
query.type = "portfolioitem"
query.fetch = "Name,FormattedID"
query.workspace = {"_ref" => "https://rally1.rallydev.com/slm/webservice/v2.0/workspace/11111" }
query.project = {"_ref" => "https://rally1.rallydev.com/slm/webservice/v2.0/project/2222" }
query.query_string = "(FormattedID = \"F22\")"
result = #rally.find(query)
feature = result.first
field_updates={"PortfolioItem" => feature}
new_s.update(field_updates)
Related
What is the best aproace of enriching c5 user's attributes.
I have non C5 table with users information this information was created on old cms (non c5), and im now building new site with c5 would like to know best aproach of migrating users.
Is it good idea to use SQL query or should i use php script for enriching, I already created users in to c5 and manualy added email addresses for "anchor point" for later enrichment.
Would be realy glad if someone could tell or maby could lead to some examples.
finaly managed by myself, its rather simple:
i exported external users to php array and used c5 user functions to add users and after enrich them my example:
$external_users = array({
array('id'=>'1', 'name'='JON', 'email'=>'blank#blank.blank', 'last_name'=>'DOE', 'attr1'=>'smthing', 'attr2'=>'123'),
array(...), ...
});
foreach($external_users as $singleUser_data){
$email = $singleUser_data['email'];
$ui = UserInfo::getByEmail($email);
if (!is_null($ui)) {
// Email is already in use, so let's not create the user
return;
}
$userData['uName'] = $singleUser_data['name']." ".$singleUser_data['lastname'];
//users later need to reset password
$userData['uPassword'] = 'asd52465465456454asd';
$userData['uPasswordConfirm'] = 'asd52465465456454asd';
//user registererd
$ui = UserInfo::register($userData);
set_new_user_group($ui);
enrichAtributes($ui, $singleUser_data);
}
function set_new_user_group($ui){
// assign the new user to a group
$g = Group::getByName('GroupName');
$u = $ui->getUserObject();
$u->enterGroup($g);
}
function enrichAtributes($ui, $singleUser_data){
$ui->setAttribute('atr_handler1', $singleUser_data['attr1']);
$ui->setAttribute('atr_handler2', $singleUser_data['attr2']);
}
Resource:
User registration programaticly and seting group
User information documentation (setting attributes)
I am trying to bulk upload 'Opportunities' into Salesforce using PHP Toolkit 20.0 and the Enterprise SOAP API.
The way I have found to do it is to create an Opportunity object and then create it in Salesforce via the SOAP API, then on the response I take the Id and use that for each 1..n OpportunityLineItems that exists for that Opportunity.
This isn't very efficient as it use 2 SOAP API calls and when done in bulk uses a lot of resources and is liable to timeouts. (I do not want to reduce the amount sent in one go as the API calls are limited)
Therefore is there a way to create both the Opportunity and it's OpportunityLineItems in a single API call?
I tried the following:
$opp = new stdClass();
$opp->Name = 'Opp1';
$opp->StageName = 'Closed Won';
$opp->Account = new stdClass();
$opp->Account->Custom_ID__c = '1234';
$opp->Pricebook2Id = '...';
$opp->OpportunityLineItems = array();
$opp->OpportunityLineItems[0] = new stdClass();
$opp->OpportunityLineItems[0]->Description = 'Product name';
$opp->OpportunityLineItems[0]->Quantity = 1;
$opp->OpportunityLineItems[0]->UnitPrice = 10.00;
...
$opp->OpportunityLineItems[n] = new stdClass();
$opp->OpportunityLineItems[n]->Description = 'Product name';
$opp->OpportunityLineItems[n]->Quantity = 1;
$opp->OpportunityLineItems[n]->UnitPrice = 10.00;
But it resulted in:
INVALID_FIELD: No such column 'OpportunityLineItems' on entity 'Opportunity'. If you are attempting to use a custom field, be sure to append the '__c' after the custom field name. Please reference your WSDL or the describe call for the appropriate names.
Which was to be expected as the WSDL file states that OpportunityLineItems is of type tns:QueryResult rather than an ens:
EDIT: Complete overhaul to show how multiple opps can be added at same time. Should be useful if you can stagger their creation somehow (store them in your local database and upload only when you got a couple/sufficient time has passed/user pressed "flush the queue" button).
Warning, the code actually looks more scary now, you might be better of checking previous version in edit history first.
It won't be too hard to create an Apex class that would accept incoming request with 2 parameters and attempt to insert them. Go to Setup->Develop->Classes->New and try this:
global with sharing class OpportunityLinkedInsert{
static webservice Opportunity insertSingle(Opportunity opp, OpportunityLineItem[] lines){
if(opp == null || lines == null){
throw new IntegrationException('Invalid data');
}
Opportunity[] result = insertMultiple(new List<Opportunity>{opp}, new List<List<OpportunityLineItem>>{lines});
return result[0]; // I imagine you want the Id back :)
}
/* I think SOAP doesn't handle method overloading well so this method has different name.
'lines' are list of lists (jagged array if you like) so opps[i] will be inserted and then lines[i] will be linked to it etc.
You can insert up to 10,000 rows in one go with this function (remember to count items in both arrays).
*/
static webservice List<Opportunity> insertMultiple(List<Opportunity> opps, List<List<OpportunityLineItem>> lines){
if(opps == null || lines == null || opps.size() == 0 || opps.size() != lines.size()){
throw new IntegrationException('Invalid data');
}
insert opps;
// I need to flatten the structure before I insert it.
List<OpportunityLineItem> linesToInsert = new List<OpportunityLineItem>();
for(Integer i = 0; i < opps.size(); ++i){
List<OpportunityLineItem> linesForOne = lines[i];
if(linesForOne != null && !linesForOne.isEmpty()){
for(Integer j = 0; j < linesForOne.size(); ++j){
linesForOne[j].OpportunityId = opps[i].Id;
}
linesToInsert.addAll(linesForOne);
}
}
insert linesToInsert;
return opps;
}
// helper class to throw custom errors
public class IntegrationException extends Exception{}
}
You'll also need an unit test class before this can go to your production organisation. Something like that should do (needs to be filled with couple more things before being 100% usable, see this question for more info).
#isTest
public class OpportunityLinkedInsertTest{
private static List<Opportunity> opps;
private static List<List<OpportunityLineItem>> items;
#isTest
public static void checSingleOppkErrorFlow(){
try{
OpportunityLinkedInsert.insertSingle(null, null);
System.assert(false, 'It should have failed on null values');
} catch(Exception e){
System.assertEquals('Invalid data',e.getMessage());
}
}
#isTest
public static void checkMultiOppErrorFlow(){
prepareTestData();
opps.remove(1);
try{
OpportunityLinkedInsert.insertMultiple(opps, items);
System.assert(false, 'It should have failed on list size mismatch');
} catch(Exception e){
System.assertEquals('Invalid data',e.getMessage());
}
}
#isTest
public static void checkSuccessFlow(){
prepareTestData();
List<Opportunity> insertResults = OpportunityLinkedInsert.insertMultiple(opps, items);
List<Opportunity> check = [SELECT Id, Name,
(SELECT Id FROM OpportunityLineItems)
FROM Opportunity
WHERE Id IN :insertResults
ORDER BY Name];
System.assertEquals(items[0].size(), check[0].OpportunityLineItems.size(), 'Opp 1 should have 1 product added to it');
System.assertEquals(items[1].size(), check[0].OpportunityLineItems.size(), 'Opp 3 should have 1 products');
}
// Helper method we can reuse in several tests. Creates 2 Opportunities with different number of line items.
private static void prepareTestData(){
opps = new List<Opportunity>{
new Opportunity(Name = 'Opp 1', StageName = 'Prospecting', CloseDate = System.today() + 10),
new Opportunity(Name = 'Opp 2', StageName = 'Closed Won', CloseDate = System.today())
};
// You might have to fill in more fields here!
// Products are quite painful to insert with all their standard/custom pricebook dependencies etc...
items = new List<List<OpportunityLineItem>>{
new List<OpportunityLineItem>{
new OpportunityLineItem(Description = 'Opp 1, Product 1', Quantity = 1, UnitPrice = 10)
},
new List<OpportunityLineItem>{
new OpportunityLineItem(Description = 'Opp 2, Product 1', Quantity = 1, UnitPrice = 10),
new OpportunityLineItem(Description = 'Opp 2, Product 2', Quantity = 1, UnitPrice = 10),
new OpportunityLineItem(Description = 'Opp 2, Product 3', Quantity = 1, UnitPrice = 10)
}
};
}
}
That's pretty much in terms of Apex code.
If either of inserts will fail you'll get a SOAP Exception back. This is also a bit better in terms of transactions, ACID etc - if insert of your line items will fail, are you prepared to clean it up from PHP side? What if some automated email notifications etc. were set up in Salesforce and already sent? Having it in one call to Apex will make sure whole request will be rolled back, pretty much like stored procedures work in the databases.
Try to create these classes in sandbox, then locate first one on the list of classes. It will have a link to generate a WSDL file which you can use to generate your PHP classes.
Going to the second one you'll see a "Run Tests" button. You'll have to make sure the test passes before pushing it to your production org - but that's whole new world of programming on the platform for you :)
I am having a tough time and have spent like 4 hrs trying to debug this. I am new to PHP, but did not expect this to be so hard.
This is the code, i am trying to update a contact table. i tried upsert and update nothign seems to work
this is the update" version of the code.
$id = '003A000000XRVFxIAP';
$updateFields = array (
'Id' => $id,
'MailingCity' => 'New York',
'MailingState' => 'NY'
);
$sObject1 = new SObject();
//$sObject1->fields = $updateFields;
//$sObject1->MailingCity= 'New York';
$sObject1->type = 'Contact';
try{
$updateResponse = $client->update(array($sObject1),'Contact');
$myID = $updateResponse->id;
}
Strict Standards: Creating default object from empty value in C:\xampp\htdocs\Proj1\ForceToolkit\soapclient\SforceEnterpriseClient.php on line 89 INVALID_FIELD: No such column 'fields' on entity 'Contact'. If you are attempting to use a custom field, be sure to append the '__c' after the custom field name. Please reference your WSDL or the describe call for the appropriate names. Error Info SoapFault exception: [sf:INVALID_FIELD] INVALID_FIELD: No such column 'fields' on entity 'Contact'. If you are attempting to use a custom field, be sure to append the '__c' after the custom field name. Please reference your WSDL or the describe call for the appropriate names. in C:\xampp\htdocs\Proj1\ForceToolkit\soapclient\SforceBaseClient.php:508 Stack trace: #0 C:\xampp\htdocs\Proj1\ForceToolkit\soapclient\SforceBaseClient.php(508): SoapClient->__call('update', Array) #1 C:\xampp\htdocs\Proj1\ForceToolkit\soapclient\SforceBaseClient.php(508): SoapClient->update(Object(stdClass))
#2 C:\xampp\htdocs\Proj1\ForceToolkit\soapclient\SforceEnterpriseClient.php(90): SforceBaseClient->_update(Object(stdClass))
#3 C:\xampp\htdocs\Proj1\createAccount.php(95): SforceEnterpriseClient->update(Array, 'Contact') #4 {main}
Looking at your trace you appear to be using the enterprise client and I can assume enterprise WSDL. This is strongly typed and you should be using the WSDL specific to your Salesforce org. If you are not using the WSDL downloaded from your org it will not have the correct objects and fields defined within it.
I would recommend using the partner client and partner WSDL. This is loosely typed and far more flexible. It would be easier to work with particularly if you aren't familiar with the PHP or the webservices.
The following should do your update...
$sObject1 = new stdClass();
$sObject1->type = 'Contact';
$sObject1->Id = $id;
$sObject1->fields['MailingCity'] = 'New York';
$sObject1->fields['MailingState'] = 'NY';
try
{
$updateResponse = $client->update( array( $sObject1 ) );
}
catch( Exception $exception )
{
// Do something
}
Note that the Id is a property of $sObject and not a value in the fields array. Also there is no need to specify the 'Contact' in your update call as you have it set in the type property of $sObject.
When using the Enterprise WSDL, don't create a new SObject, just create a new stdClass. See the examples in the PHP Getting Started Guide; SObjects are only for use with the partner WSDL.
I have encountered the same issue updating while using the Enterprise client. I experienced the same issue while updating a custom field on an Account object.
The issue that I had with the SObject was that it tried to update a parameter called 'fields' during the update. With the Enterprise WSDL not including that field, I used unset() to remove the 'fields' attribute from the SObject.
I appreciate this is a bit of a hacky solution, but it could come in useful for others that encounter this issue.
$sUpdateObject = new SObject();
$sUpdateObject->id = $record->Id;
$sUpdateObject->MyCustomField__c = 0;
unset($sUpdateObject->fields);
$updateResponse = $mySforceConnection->update(array($sUpdateObject), 'Account');
print_r($upsertResponse);
The documentation for Netsuite is quite lacking, they cover the basics and then let you loose to explore. Anyone without a vast knowledge of PHP trying to use their php toolkit would be on their knees begging for mercy.
At any point throughout this whole project it's been trail and error and trying to make sense out of everything until stuff started to work.
I'm stumped on assigning custom fields to sales orders, I know it has to be an object of an object of an object in order for it to tier down the xml for the soap to take over but what with what with what?
I have some code I worked that is getting somewhere but it is complaining it's not the right RecordRef type. If anyone worked with Netsuite and feels my pain please lend me your knowledge before I pull out all my hair.
Thanks in advance.
Code:
$customFields = array('internalId' => 'custbody_new_die_yn','value' => array('name' => 'custbody_new_die_yn','internalId' => 'NO'));
$customObject = new nsComplexObject("SelectCustomFieldRef");
$customObject->setFields($customFields);
$salesOrderFields = array(
'entity' => new nsRecordRef(array('internalId' => $userId)),
'paymentMethod' => array('internalId' => 8),
'ccNumber' => 4111111111111111,
'ccExpireDate' => date("c", mktime(0,0,0,11,1,2011)),
'ccName' => 'Test Testerson',
'itemList' => array(
'item' => array(
'item' => array('internalId' => 5963),
'quantity' => 5
)
),
'department' => new nsRecordRef(array('internalId' => 1)),
'class' => new nsRecordRef(array('internalId' => 47)),
'customFieldList' => $customObject
);
I am not familiar using PHP with Netsuite but I have done a good amount of c#/.net Netsuite work. As Craig mentioned I find it much easier using a language such c#/.net with a Visual Studio generated interface to figure out what is available in the Netsuite SuiteTalk web service API.
There is a fair amount of documentation around this stuff in the NetSuite Help Center - by no means everythign you will need but a good start. Netsuite Help Center
Check out the SuiteFlex/SuiteTalk (Web Services) section specifically this page on Ids & References.
Using Internal Ids, External Ids, and References
With that said I will try to help with a .net example & explanation of adding a custom field to a Sales Order.
Here are a few examples of adding different CustomFieldRefs:
//A list object to store all the customFieldRefs
List<CustomFieldRef> oCustomFieldRefList = new List<CustomFieldRef>();
//List or Record Type reference
SelectCustomFieldRef custbody_XXX_freight_terms = new SelectCustomFieldRef();
custbody_XXX_freight_terms.internalId = "custbody_XXX_freight_terms";
ListOrRecordRef oFreightTermsRecordRef = new ListOrRecordRef();
oFreightTermsRecordRef.internalId = <internalId of specific record in Netsuite>;
//See the References link above for more info on this - trying to figure out typeId caused me a lot of pain.
oFreightTermsRecordRef.typeId = <internalId of the List Record Type in Netsuite>;
custbody_XXX_freight_terms.value = oFreightTermsRecordRef;
oCustomFieldRefList.Add(custbody_XXX_freight_terms);
//Freeform text sorta field
StringCustomFieldRef objStringCustomFieldRef = new StringCustomFieldRef();
objStringCustomFieldRef.internalId = "custbody_XXX_tracking_link";
objStringCustomFieldRef.value = "StringValue";
oCustomFieldRefList.Add(objStringCustomFieldRef);
//Checkbox field type
BooleanCustomFieldRef custbody_XXX_if_fulfilled = new BooleanCustomFieldRef();
custbody_XXX_if_fulfilled.internalId = "custbody_XXX_if_fulfilled";
custbody_XXX_if_fulfilled.value = true;
oCustomFieldRefList.Add(custbody_XXX_if_fulfilled);
//By far the most complicated example a multi-select list referencing other records in Netsuite
MultiSelectCustomFieldRef custrecord_XXX_transaction_link = new MultiSelectCustomFieldRef();
//internal id of field you are updating
custrecord_XXX_transaction_link.internalId = "custrecord_XXX_transaction_link";
List<ListOrRecordRef> oListOrRecordRefList = new List<ListOrRecordRef>();
ListOrRecordRef oListOrRecordRefItemFulfillment = new ListOrRecordRef();
oListOrRecordRefItemFulfillment.name = "Item Fulfillment";
oListOrRecordRefItemFulfillment.internalId = <ItemFulfillmentInternalId>;
//Item Fulfillment is record type (Transaction -30) - this is from the above Reference links
oListOrRecordRefItemFulfillment.typeId = "-30";
oListOrRecordRefList.Add(oListOrRecordRefItemFulfillment);
ListOrRecordRef oListOrRecordRefSalesOrder = new ListOrRecordRef();
oListOrRecordRefSalesOrder.name = "Sales Order";
oListOrRecordRefSalesOrder.internalId = <SalesOrderInternalId>;
//Sales Order is record type (Transaction -30) - this is from the above Reference links
oListOrRecordRefSalesOrder.typeId = "-30";
oListOrRecordRefList.Add(oListOrRecordRefSalesOrder);
//Add array of all the ListOrRecordRefs to the MultiSelectCustomFieldRef
custrecord_XXX_transaction_link.value = oListOrRecordRefList.ToArray();
oCustomFieldRefList.Add(custrecord_XXX_transaction_link);
//And then add all these to the Custom Record List (Array) on the Sales Order Record
objSalesOrder.customFieldList = oCustomFieldRefList.ToArray();
From what I can tell in your above example I think your issue is with the ListOrRecordRef typeId. Its hard to tell from your example what typeId you are referencing but if you can figure that out and set the TypeId on your SelectCustomFieldRef I think that should fix your issue.
The Custom Field Ref Internal ID is the reference ID on the record you are trying to update. This can be found in the Transaction Body fields for that record within Netsuite.
The Internal ID for the ListOrRecordRef is the internal ID for the actual list item or record that you want to attach to the previously mentioned record
The typeID for the ListOrRecordRef is the internal ID for the custom list/record. This is the parent ID for the previous internal ID, and is not inherently tied to the original record.
I made a private section on a drupal site by writing a module that checks the RERQUEST_URI for the section as well as user role. The issue I am running into now is how to prevent those nodes/views from appearing in the search.
The content types used in the private section are used in other places in the site.
What's the best way to get Druapl search to ignore the content/not index/not display it in search results?
There is a wonderful article that explains just this on the lullabot site.
It's worth reading the comments to the post too, because people there suggested alternate ways of doing that, also by mean of contrib modules (rather than implementing some hooks in your own code). Code for D6 is in the comment as well.
HTH!
The lullabot article is a bit outdated and contains many blunt approaches. It also contains the answer in the comments - the Search Restrict module which works for DP6 and allows fine-grained and role-based control. Everything else either prevents content from being indexed, which may not be desirable if there are different access levels to content, or affects all search queries equally, which will also not work if there are different access levels.
If the content types used within the Private section are also used elsewhere how are you hoping to filter them out of the search results (note that I've not looked at the lullabot article by mac yet).
Basically, if you look at the details of two nodes, one private and one public, what differentiates them?
Note: I'm assuming that you want the nodes to appear to users with access to the Private area but not to 'anonymous' users.
For Drupal 7.
You can hide the node from search results by using custom field. In my case, I have created a custom field in the name of Archive to the desired content type and with the help of that custom field you can write the my_module_query_alter functionality.
Code
function my_module_query_alter(QueryAlterableInterface $query) {
$is_search = $is_node_search = FALSE;
$node_alias = FALSE;
foreach ( $query->getTables() as $table ) {
if ( $table['table'] == 'search_index' || $table['table'] == 'tracker_user') {
$is_search = TRUE;
}
if ( $table['table'] == 'node' || $table['table'] == 'tracker_user') {
$node_alias = $table['alias'];
$is_node_search = TRUE;
}
}
if ( $is_search && $is_node_search ) {
$nids = [];
// Run entity field query to get nodes that are 'suppressed from public'.
$efq = new EntityFieldQuery();
$efq->entityCondition('entity_type', 'node')
->fieldCondition('field_archive', 'value', 1, '=');
$result = $efq->execute();
if ( isset($result['node']) ) {
$nids = array_keys($result['node']);
}
if ( count($nids) > 0 ) {
$query->condition(sprintf('%s.nid', $node_alias), $nids, 'NOT IN');
}
}
}