A match made in the cloud

We’ve recently delivered an API for the Cash Matching function, that will allow our customers, partners and our Product Services Team to leverage our Cash Matching module in a more automated and customized way. The Cash Matching API will allow you to create your own cash matching rules and embed the cash matching process into your processes built on the Salesforce Platform.

This article is intended for Apex developers already familiar with FinancialForce object APIs who want a quick insight into a way of using the Cash Matching API. For information about the capability to utilise more native Apex DML features with our objects, see Using the Salesforce API.

There are many ways in which you can use the Cash Matching API. A sample scenario is provided below:

Sample Scenario: Credit Card Payments

Your company sells products over the phone and accepts credit cards as payment. A Credit Card Transaction custom object captures the details of credit card sales. A custom invoicing process is required to perform the following tasks automatically:

  • — Query for credit card transaction records available for invoicing
  • — Generate an appropriate invoice and issue it via email to the customer
  • — Generate an appropriate cash entry reflecting the payment
  • — Match the two transactions using the Cash Matching API

The following screenshot shows a sample credit card transaction custom object used by the code illustrated in this article:

CashMatching-CreditCardScreenshot1

IMPLEMENTATION TIP: The process described above potentially deals with many credit card transactions, each resulting in its own sales invoice, cash entry and match processes. We therefore recommend that you use the scalability provided by the Salesforce Platform Batch Apex technology on the Salesforce Platform.

  1. Create the sales invoice and cash entries from the credit card transaction record and then use the applicable Post APIs. Cash matching requires available account transaction lines to match against each other. The following code sample uses a DML approach:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    // Generate appropriate Invoice
    c2g__codaInvoice__c salesInvoice = new c2g__codaInvoice__c();
    salesInvoice.c2g__Account__c = ccTransaction.Customer__c;
    List<c2g__codaInvoiceLineItem__c> salesInvoiceLineItems = new List<c2g__codaInvoiceLineItem__c>();
    for(CreditCardTransactionLineItem__c ccTranLine : ccTransaction.CreditCardTransactionLineItems__r)
    {
        c2g__codaInvoiceLineItem__c invoiceLineItem = new c2g__codaInvoiceLineItem__c();
        invoiceLineItem.c2g__Product__c = ccTranLine.Product__c;
        invoiceLineItem.c2g__Quantity__c = 1;
        invoiceLineItem.c2g__UnitPrice__c = ccTranLine.Amount__c;
        // Tax in this example is inclusive in the Amount above
        invoiceLineItem.c2g__TaxValue1__c = 0;
        // Disable defaulting from Product for this example
        invoiceLineItem.ffbilling__DeriveUnitPriceFromProduct__c = false;
        // Disable default tax calculation for this example
        invoiceLineItem.ffbilling__DeriveTaxRate1FromCode__c = false;
        salesInvoiceLineItems.add(invoiceLineItem);
    }
    // Execute DML to insert Invoice
    insert salesInvoice;
    for(c2g__codaInvoiceLineItem__c salesInvoiceLineItem : salesInvoiceLineItems)
    {
        salesInvoiceLineItem.c2g__Invoice__c = salesInvoice.Id;
    }
    insert salesInvoiceLineItems;
    // Generate appropriate Cash Entry
    c2g__codaCashEntry__c cashEntry = new c2g__codaCashEntry__c();
    cashEntry.c2g__BankAccount__c = BANK_ACCOUNT_ID;
    cashEntry.c2g__Period__c = PERIOD_ID;
    cashEntry.c2g__CashEntryCurrency__c = CURRENCY_ID;
    List<c2g__codaCashEntryLineItem__c> cashEntryLineItems = new List<c2g__codaCashEntryLineItem__c>();
    c2g__codaCashEntryLineItem__c cashEntryLineItem = new c2g__codaCashEntryLineItem__c();
    cashEntryLineItem.c2g__Account__c = ccTransaction.Customer__c;
    cashEntryLineItem.c2g__CashEntryValue__c = ccTransaction.TransactionAmount__c;
    cashEntryLineItems.add(cashEntryLineItem);
    //Execute DML to insert Cash Entry
    insert cashEntry;
    for(c2g__codaCashEntryLineItem__c cashEntryLineItem2 : cashEntryLineItems)
    {
        cashEntryLineItem2.c2g__CashEntry__c = cashEntry.Id;
    }
    insert cashEntryLineItems;
  2. Post the invoice and cash entry and query the transaction line references to pass to the matching API:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    // Post the Invoice
    List<c2g.CODAAPICommon.Reference>
    invoiceRefs = new List<c2g.CODAAPICommon.Reference>();
    invoiceRefs.add(c2g.CODAAPICommon.getRef(salesInvoice.Id, null));
    c2g.CODAAPISalesInvoice_6_0.BulkPostInvoice(null, invoiceRefs);
    // Post the Cash Entry
    List<c2g.CODAAPICommon.Reference>
    cashEntryRefs = new List<c2g.CODAAPICommon.Reference>();
    cashEntryRefs.add(c2g.CODAAPICommon.getRef(cashEntry.Id, null));
    c2g.CODAAPICashEntry_6_0.BulkPostCashEntry(null, cashEntryRefs);
    // Query for the Transaction Line Items
    // (resulting from the postings above) to match
    List<String> documentRefs = new List<String>();
    documentRefs.add([select Id, Name from c2g__codaInvoice__c
    where Id = :salesInvoice.Id].Name);
    documentRefs.add([select Id, Name from c2g__codaCashEntry__c
    where Id = :cashEntry.Id].Name);
    List<c2g__codaTransactionLineItem__c> transactionLinesToMatch = [
        select
            Id,
            c2g__AccountValue__c
        from c2g__codaTransactionLineItem__c
        where
            c2g__Transaction__r.c2g__DocumentNumber__c in :documentRefs and
            c2g__LineType__c = 'Account'
    ];
  3. Call the Cash Matching API:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // Match Invoice with Cash Entry
    c2g.CODAAPICommon_6_0.Context context = new c2g.CODAAPICommon_6_0.Context();
    c2g.CODAAPICashMatchingTypes_6_0.Configuration configuration = new c2g.CODAAPICashMatchingTypes_6_0.Configuration();
    configuration.Account = c2g.CODAAPICommon.getRef(ccTransaction.Customer__c, null);
    configuration.MatchingCurrencyMode = c2g.CODAAPICashMatchingTypes_6_0.enumMatchingCurrencyMode.Account;
    configuration.MatchingDate = System.today();
    configuration.MatchingPeriod = c2g.CODAAPICommon.getRef(CURRENT_PERIOD_ID, null);
    List<c2g.CODAAPICashMatchingTypes_6_0.Item> items = new List<c2g.CODAAPICashMatchingTypes_6_0.Item>();
    for (c2g__codaTransactionLineItem__c transactionLine : transactionLinesToMatch)
    {
        c2g.CODAAPICashMatchingTypes_6_0.Item item = new c2g.CODAAPICashMatchingTypes_6_0.Item();
        item.TransactionLineItem = c2g.CODAAPICommon.getRef(transactionLine.Id, null);
        item.Paid = transactionLine.c2g__AccountValue__c;
        item.Discount = 0; // No discount in this example
        item.WriteOff = 0// No write off in this example
        items.add(item);
    }
    c2g.CODAAPICashMatchingTypes_6_0.Analysis analisysInfo = new c2g.CODAAPICashMatchingTypes_6_0.Analysis();
    // analisysInfo.DiscountGLA = ...
    // analisysInfo.DiscountDimension1..4 = ...
    // analisysInfo.WriteOffGLA = ...
    // analisysInfo.WriteOffDimension1..4 = ...
    // analisysInfo.CurrencyWriteOffGLA = ...
    // analisysInfo.CurrencyWriteOffDimension1..4 = ...
    c2g.CODAAPICommon.Reference matchReference = c2g.CODAAPICashMatching_6_0.Match(context, configuration, items, analisysInfo);
  4. Email the invoice and update the original Credit Card Transaction record with the matching reference returned from the API, as well as references to the Sales Invoice and Cash Entry created earlier:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Email the Invoice to the customer (the email template should
    // detect the invoice is paid and adjust the wording accordingly)
    c2g.CODAAPISalesInvoice_6_0.PrintInvoiceToEmail(
        null,
        c2g.CODAAPICommon.getRef(salesInvoice.Id, null)
    );
    // Update Credit Card Transaction with references to the above
    ccTransaction.Invoiced__c = true;
    ccTransaction.SalesInvoice__c = salesInvoice.Id;
    ccTransaction.CashEntry__c = cashEntry.Id;
    ccTransaction.MatchingReference__c = matchReference.Id;
    update ccTransaction;

The following screenshot shows how the invoice and transaction appear on the Credit Card Transaction object once the process is complete.

CashMatching-CreditCardScreenshot2

In our example, the matching requirements are quite simple however you can use the calling code to decide which transaction references are passed to the API. The possibilities for cash matching rules are now only limited by your imagination as a developer!

Automation is not the only use for the Cash Matching API. You can build your own cash matching user interface, as we have done recently. The new FinancialForce Extended Cash Matching package (currently available in Pilot) is shown below. This uses the Cash Matching API described in this article.

CashMatching-ExtendedCashMatchingUI

IMPORTANT USAGE NOTICE PLEASE READ 

Copyright © 2011, FinancialForce.com, inc

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • Neither the name of the FinancialForce.com, inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.