2012-11-07 32 views
2

我正嘗試使用PHP Toolkit 20.0和Enterprise SOAP API將'Opportunities'批量上傳到Salesforce。Salesforce:創建OpportunityLineItems作爲PHP機會的一部分

我發現這樣做的方式是創建一個Opportunity對象,然後通過SOAP API在Salesforce中創建它,然後在響應中我採用Id並將其用於存在的每個1..n的OpportunityLineItems機會。

這不是非常有效,因爲它使用2個SOAP API調用,並且在批量完成時會佔用大量資源並且可能會超時。 (由於API調用是有限的,我不想減少一次性發送的金額)

因此,是否有一種方法可以在單個API調用中創建商機和它的OpportunityLineItems?

我試過如下:

$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; 

但它導致了:
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.
這是可預期爲OpportunityLineItems是tns:QueryResult型的,而不是一個ens:

+0

真的很好的問題,我很好奇!但是,如果API請求的數量確實令人擔憂,那麼您是否可以僅僅支付更多的用戶許可證,而每個用戶都可以給予更多的1-5K電話費用? https://eu1.salesforce.com/help/doc/en/integrate_api_rate_limiting.htm。另外 - 是創建Apex webservice類,可以接受機會和OLI數組一個有效的選擇嗎? – eyescream

+0

@eyescream老闆對Licenses說不,Apex webservice是一個不錯的主意,我會研究一下,但我現在沒有技能,所以PHP解決方案仍然是理想選擇。 –

回答

5
WSDL文件狀態

編輯:完成大修,以顯示如何在同一時間添加多個opps。如果你可以以某種方式錯開它們的創建(將它們存儲在本地數據庫中,並且只有當你有足夠的時間/足夠的時間已經過去/用戶按下「沖洗隊列」按鈕時)纔會有用。

警告,代碼實際上現在看起來更可怕,您可能會首先在編輯歷史記錄中檢查previous version

創建一個Apex類不會太難,它會接受帶有2個參數的傳入請求並嘗試插入它們。進入設置 - >培養─>類 - >新建,試試這個:

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{} 
} 

您還需要在此之前的單元測試類可以去你的生產組織。類似的東西應該這樣做(在100%可用之前需要充滿更多的東西,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) 
      } 
     }; 
    } 
} 

這就是在頂點代碼方面非常。

如果任何一個插入操作失敗,您將返回一個SOAP異常。這在交易方面也有所改善,ACID等 - 如果您的訂單項插入失敗,您準備從PHP端清理它嗎?如果在Salesforce中設置了一些自動電子郵件通知等並且已經發送了,會怎麼樣?通過一次調用Apex將確保整個請求將被回滾,就像存儲過程在數據庫中一樣。

嘗試在沙箱中創建這些類,然後在類的列表中找到第一個類。它將有一個鏈接來生成一個WSDL文件,您可以使用它來生成您的PHP類。 轉到第二個,你會看到一個「運行測試」按鈕。你必須確保測試通過之後再推到你的生產組織 - 但這是全新的世界programming on the platform爲你:)

+0

謝謝你。我還沒有得到它的工作(技能問題),但它是正確的方式去。 –

+0

再一次感謝你,這正是我們所需要的。我將這個和單元測試添加到了我的沙箱中。進行測試,獲得WSDL文件,然後嘗試訪問它,但得到了Salesforce UNKNOWN_EXCEPTION,我已向Salesforce支持報告。 –

+0

@PhilCarter很奇怪,在你等待它們的時候試試我的:http://pastebin.com/uAKUxAk7你必須修改文件底部的端點(我的指向na5.salesforce.com上的「Developer Edition」 )。請參閱http://stackoverflow.com/a/13184262/313628以獲取更多解釋。你可能會亂用它,因爲我可以看到它有我所有的對象......在底部試驗 :) – eyescream

相關問題