2

PHPToolkit20.0とEnterpriseSOAPAPIを使用して、「Opportunities」をSalesforceに一括アップロードしようとしています。

私が見つけた方法は、Opportunityオブジェクトを作成し、SOAP APIを介してSalesforceで作成し、応答で、IdそのOpportunityに存在する1..nOpportunityLineItemsごとにそれを使用することです。

これは、2つのSOAP API呼び出しを使用し、一括で実行すると多くのリソースを使用し、タイムアウトが発生しやすいため、あまり効率的ではありません。(API呼び出しが制限されているため、一度に送信される量を減らしたくありません)

したがって、単一のAPI呼び出しでOpportunityとその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.
ました。WSDLファイルには、OpportunityLineItemsがタイプtns:QueryResultではなくタイプであると記載されているため、これは予想されていました。ens:

4

1 に答える 1

4

編集: 複数の opps を同時に追加する方法を示す完全なオーバーホール。何らかの方法でそれらの作成をずらすことができる場合に役立つはずです(それらをローカルデータベースに保存し、カップルを取得した/十分な時間が経過した/ユーザーが「キューをフラッシュする」ボタンを押した場合にのみアップロードします)。

警告、コードは実際にはより恐ろしく見えます。最初に編集履歴で以前のバージョンを確認したほうがよいかもしれません。

2 つのパラメータを持つ受信リクエストを受け入れ、それらを挿入しようとする Apex クラスを作成するのはそれほど難しくありません。Setup->Develop->Classes->New に移動して、これを試してください:

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%使用できるようにするには、さらにいくつかのことで満たす必要があります。詳細については、この質問を参照してください)。

@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)
            }
        };
    }
}

これは、Apex コードに関してはほぼ同じです。

いずれかの挿入が失敗すると、SOAP 例外が返されます。これは、トランザクション、 ACIDなどの点でも少し優れています。行項目の挿入が失敗した場合、PHP 側からクリーンアップする準備はできていますか? 自動メール通知などが Salesforce で設定され、すでに送信されている場合はどうなりますか? Apex への 1 回の呼び出しでそれを行うと、データベースでストアド プロシージャが機能するのとほぼ同じように、要求全体が確実にロールバックされます。

これらのクラスをサンドボックスで作成してから、クラスのリストで最初のクラスを見つけてください。PHP クラスの生成に使用できる WSDL ファイルを生成するためのリンクがあります。2 番目のものに移動すると、[Run Tests] ボタンが表示されます。テストを本番組織にプッシュする前に、テストが合格することを確認する必要がありますが、それはプラットフォーム上でのプログラミングのまったく新しい世界です :)

于 2012-11-07T15:50:02.117 に答える