---
schema_version: "1"
id: 42df56337fb7ec400d4773e2ec5e6c64bf1af8bd9d79ea763fdee26fe521b41f
title: Shipping Invoice Example Code
collection: legacydeveloper
version: current
locale: en-us
portal_url: https://docs.salesforce.com/legacydeveloper/current/en-us/apexcode/apex_shopping_cart_example_code.md
canonical_url: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_shopping_cart_example_code.htm
source_site: developer.salesforce.com
content_type: documentation
content_hash: c01e55676e796fa87d568d673834e27c34062c697b635fee6fc5716ea73af8c4
---
# Shipping Invoice Example Code

These triggers and test class make up the shipping invoice example application:

- [Calculate trigger](#sc_calculate_trigger_code)
- [ShippingDiscount trigger](#sc_shippingdiscount_trigger_code)
- [Test class](#sc_testshoppingcart_test_code)

## Calculate Trigger

trigger calculate on Item\_\_c (after insert, after update, after delete) {

// Use a map because it doesn't allow duplicate values

Map<ID, Shipping\_Invoice\_\_C> updateMap = new Map<ID, Shipping\_Invoice\_\_C>();

// Set this integer to -1 if we are deleting
Integer subtract ;

// Populate the list of items based on trigger type
List<Item\_\_c> itemList;
    if(trigger.isInsert || trigger.isUpdate){
        itemList = Trigger.new;
        subtract = 1;
    }
    else if(trigger.isDelete)
    {
        // Note -- there is no trigger.new in delete
        itemList = trigger.old;
        subtract = -1;
    }

// Access all the information we need in a single query 
// rather than querying when we need it.
// This is a best practice for bulkifying requests

set<Id> AllItems = new set<id>();

for(item\_\_c i :itemList){
// Assert numbers are not negative.  
// None of the fields would make sense with a negative value

Assert.isTrue(i.quantity\_\_c > 0, 'Quantity must be positive');
Assert.isTrue(i.weight\_\_c >= 0, 'Weight must be non-negative');
Assert.isTrue(i.price\_\_c >= 0, 'Price must be non-negative');

// If there is a duplicate Id, it won't get added to a set
AllItems.add(i.Shipping\_Invoice\_\_C);
}

// Accessing all shipping invoices associated with the items in the trigger
List<Shipping\_Invoice\_\_C> AllShippingInvoices = \[SELECT Id, ShippingDiscount\_\_c, 
                   SubTotal\_\_c, TotalWeight\_\_c, Tax\_\_c, GrandTotal\_\_c 
                   FROM Shipping\_Invoice\_\_C WHERE Id IN :AllItems WITH USER\_MODE\];
                   
// Take the list we just populated and put it into a Map.  
// This will make it easier to look up a shipping invoice
// because you must iterate a list, but you can use lookup for a map, 
Map<ID, Shipping\_Invoice\_\_C> SIMap = new Map<ID, Shipping\_Invoice\_\_C>();

for(Shipping\_Invoice\_\_C sc : AllShippingInvoices)
{
    SIMap.put(sc.id, sc);
}

// Process the list of items
    if(Trigger.isUpdate)
    {
        // Treat updates like a removal of the old item and addition of the         
        // revised item rather than figuring out the differences of each field 
        // and acting accordingly.
        // Note updates have both trigger.new and trigger.old
        for(Integer x = 0; x < Trigger.old.size(); x++)
        {
            Shipping\_Invoice\_\_C myOrder;
            myOrder = SIMap.get(trigger.old\[x\].Shipping\_Invoice\_\_C);

            // Decrement the previous value from the subtotal and weight.
            myOrder.SubTotal\_\_c -= (trigger.old\[x\].price\_\_c \* 
                                    trigger.old\[x\].quantity\_\_c);
            myOrder.TotalWeight\_\_c -= (trigger.old\[x\].weight\_\_c \* 
                                       trigger.old\[x\].quantity\_\_c);
                
            // Increment the new subtotal and weight.
            myOrder.SubTotal\_\_c += (trigger.new\[x\].price\_\_c \* 
                                    trigger.new\[x\].quantity\_\_c);
            myOrder.TotalWeight\_\_c += (trigger.new\[x\].weight\_\_c \* 
                                       trigger.new\[x\].quantity\_\_c);
        }
        
        for(Shipping\_Invoice\_\_C myOrder : AllShippingInvoices)
        {
            
            // Set tax rate to 9.25%  Please note, this is a simple example.  
            // Generally, you would never hard code values.
            // Leveraging Custom Settings for tax rates is a best practice.  
            // See Custom Settings in the Apex Developer Guide 
            // for more information.
            myOrder.Tax\_\_c = myOrder.Subtotal\_\_c \* .0925;
            
            // Reset the shipping discount
            myOrder.ShippingDiscount\_\_c = 0;
    
            // Set shipping rate to 75 cents per pound.  
            // Generally, you would never hard code values.
            // Leveraging Custom Settings for the shipping rate is a best practice.
            // See Custom Settings in the Apex Developer Guide 
            // for more information.
            myOrder.Shipping\_\_c = (myOrder.totalWeight\_\_c \* .75);
            myOrder.GrandTotal\_\_c = myOrder.SubTotal\_\_c + myOrder.tax\_\_c + 
                                    myOrder.Shipping\_\_c;
            updateMap.put(myOrder.id, myOrder);
         }
    }
    else
    { 
        for(Item\_\_c itemToProcess : itemList)
        {
            Shipping\_Invoice\_\_C myOrder;
    
            // Look up the correct shipping invoice from the ones we got earlier
            myOrder = SIMap.get(itemToProcess.Shipping\_Invoice\_\_C);
            myOrder.SubTotal\_\_c += (itemToProcess.price\_\_c \* 
                                    itemToProcess.quantity\_\_c \* subtract);
            myOrder.TotalWeight\_\_c += (itemToProcess.weight\_\_c \* 
                                       itemToProcess.quantity\_\_c \* subtract);
        }
        
        for(Shipping\_Invoice\_\_C myOrder : AllShippingInvoices)
        {
            
             // Set tax rate to 9.25%  Please note, this is a simple example.  
             // Generally, you would never hard code values.
             // Leveraging Custom Settings for tax rates is a best practice.  
             // See Custom Settings in the Apex Developer Guide 
             // for more information.
             myOrder.Tax\_\_c = myOrder.Subtotal\_\_c \* .0925;
             
             // Reset shipping discount
             myOrder.ShippingDiscount\_\_c = 0;
    
            // Set shipping rate to 75 cents per pound.  
            // Generally, you would never hard code values.
            // Leveraging Custom Settings for the shipping rate is a best practice.
            // See Custom Settings in the Apex Developer Guide 
            // for more information.
            myOrder.Shipping\_\_c = (myOrder.totalWeight\_\_c \* .75);
            myOrder.GrandTotal\_\_c = myOrder.SubTotal\_\_c + myOrder.tax\_\_c + 
                                    myOrder.Shipping\_\_c;
                                       
            updateMap.put(myOrder.id, myOrder);
    
         }
     }    
     
     // Only use one DML update at the end.
     // This minimizes the number of DML requests generated from this trigger.
     update as user updateMap.values();
}

## ShippingDiscount Trigger

trigger ShippingDiscount on Shipping\_Invoice\_\_C (before update) {
    // Free shipping on all orders greater than $100
    
    for(Shipping\_Invoice\_\_C myShippingInvoice : Trigger.new)
    {
        if((myShippingInvoice.subtotal\_\_c >= 100.00) && 
           (myShippingInvoice.ShippingDiscount\_\_c == 0))
        {
            myShippingInvoice.ShippingDiscount\_\_c = 
                         myShippingInvoice.Shipping\_\_c \* -1;
            myShippingInvoice.GrandTotal\_\_c += myShippingInvoice.ShippingDiscount\_\_c;
        }
    }
}

## Shipping Invoice Test

@IsTest
private with sharing class TestShippingInvoice{

    // Test for inserting three items at once
    @IsTest
    public static void testBulkItemInsert(){
        // Create the shipping invoice. It's a best practice to either use defaults
        // or to explicitly set all values to zero so as to avoid having
        // extraneous data in your test.
        Shipping\_Invoice\_\_C order1 = new Shipping\_Invoice\_\_C(subtotal\_\_c = 0, 
                          totalweight\_\_c = 0, grandtotal\_\_c = 0, 
                          ShippingDiscount\_\_c = 0, Shipping\_\_c = 0, tax\_\_c = 0);

        // Insert the order and populate with items
        insert Order1;
        List<Item\_\_c> list1 = new List<Item\_\_c>();
        Item\_\_c item1 = new Item\_\_C(Price\_\_c = 10, weight\_\_c = 1, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c item2 = new Item\_\_C(Price\_\_c = 25, weight\_\_c = 2, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c item3 = new Item\_\_C(Price\_\_c = 40, weight\_\_c = 3, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        list1.add(item1);
        list1.add(item2);
        list1.add(item3);
        insert list1;
        
        // Retrieve the order, then do assertions
        order1 = \[SELECT id, subtotal\_\_c, tax\_\_c, shipping\_\_c, totalweight\_\_c, 
                  grandtotal\_\_c, shippingdiscount\_\_c 
                  FROM Shipping\_Invoice\_\_C 
                  WHERE id = :order1.id WITH USER\_MODE\];
        
        Assert.areEqual(75, order1.subtotal\_\_c, 
                'Order subtotal was not $75, but was '\+ order1.subtotal\_\_c);
        Assert.areEqual(6.9375, order1.tax\_\_c, 
                'Order tax was not $6.9375, but was ' + order1.tax\_\_c);
        Assert.areEqual(4.50, order1.shipping\_\_c, 
                'Order shipping was not $4.50, but was ' + order1.shipping\_\_c);
        Assert.areEqual(6.00, order1.totalweight\_\_c, 
                'Order weight was not 6 but was ' + order1.totalweight\_\_c);
        Assert.areEqual(86.4375, order1.grandtotal\_\_c, 
                'Order grand total was not $86.4375 but was ' 
                 + order1.grandtotal\_\_c);
        Assert.areEqual(0, order1.shippingdiscount\_\_c, 
                'Order shipping discount was not $0 but was ' 
                + order1.shippingdiscount\_\_c);
    }
    
    // Test for updating three items at once
    @IsTest
    public static void testBulkItemUpdate(){

        // Create the shipping invoice. It's a best practice to either use defaults
        // or to explicitly set all values to zero so as to avoid having
        // extraneous data in your test.
        Shipping\_Invoice\_\_C order1 = new Shipping\_Invoice\_\_C(subtotal\_\_c = 0, 
                          totalweight\_\_c = 0, grandtotal\_\_c = 0, 
                          ShippingDiscount\_\_c = 0, Shipping\_\_c = 0, tax\_\_c = 0);

        // Insert the order and populate with items.
        insert as user Order1;
        List<Item\_\_c> list1 = new List<Item\_\_c>();
        Item\_\_c item1 = new Item\_\_C(Price\_\_c = 1, weight\_\_c = 1, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c item2 = new Item\_\_C(Price\_\_c = 2, weight\_\_c = 2, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c item3 = new Item\_\_C(Price\_\_c = 4, weight\_\_c = 3, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        list1.add(item1);
        list1.add(item2);
        list1.add(item3);
        insert as system list1;
        
        // Update the prices on the 3 items
        list1\[0\].price\_\_c = 10;
        list1\[1\].price\_\_c = 25;
        list1\[2\].price\_\_c = 40;
        update as system list1;
        
        // Access the order and assert items updated
        order1 = \[SELECT id, subtotal\_\_c, tax\_\_c, shipping\_\_c, totalweight\_\_c, 
                  grandtotal\_\_c, shippingdiscount\_\_c 
                  FROM Shipping\_Invoice\_\_C 
                  WHERE Id = :order1.Id WITH USER\_MODE\];

        Assert.areEqual(75, order1.subtotal\_\_c, 
                       'Order subtotal was not $75, but was '\+ order1.subtotal\_\_c);
        Assert.areEqual(6.9375, order1.tax\_\_c, 
                       'Order tax was not $6.9375, but was ' + order1.tax\_\_c);
        Assert.areEqual(4.50, order1.shipping\_\_c, 
                       'Order shipping was not $4.50, but was ' 
                       + order1.shipping\_\_c);
        Assert.areEqual(6.00, order1.totalweight\_\_c, 
                       'Order weight was not 6 but was ' + order1.totalweight\_\_c);
        Assert.areEqual(86.4375, order1.grandtotal\_\_c, 
                       'Order grand total was not $86.4375 but was ' 
                       + order1.grandtotal\_\_c);
        Assert.areEqual(0, order1.shippingdiscount\_\_c, 
                       'Order shipping discount was not $0 but was ' 
                       + order1.shippingdiscount\_\_c);
    
    }
    
    // Test for deleting items
    @IsTest
    public static void testBulkItemDelete(){

        // Create the shipping invoice. It's a best practice to either use defaults
        // or to explicitly set all values to zero so as to avoid having
        // extraneous data in your test.
        Shipping\_Invoice\_\_C order1 = new Shipping\_Invoice\_\_C(subtotal\_\_c = 0, 
                          totalweight\_\_c = 0, grandtotal\_\_c = 0, 
                          ShippingDiscount\_\_c = 0, Shipping\_\_c = 0, tax\_\_c = 0);

        // Insert the order and populate with items
        insert Order1;
        List<Item\_\_c> list1 = new List<Item\_\_c>();
        Item\_\_c item1 = new Item\_\_C(Price\_\_c = 10, weight\_\_c = 1, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c item2 = new Item\_\_C(Price\_\_c = 25, weight\_\_c = 2, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c item3 = new Item\_\_C(Price\_\_c = 40, weight\_\_c = 3, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c itemA = new Item\_\_C(Price\_\_c = 1, weight\_\_c = 3, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c itemB = new Item\_\_C(Price\_\_c = 1, weight\_\_c = 3, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c itemC = new Item\_\_C(Price\_\_c = 1, weight\_\_c = 3, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c itemD = new Item\_\_C(Price\_\_c = 1, weight\_\_c = 3, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        list1.add(item1);
        list1.add(item2);
        list1.add(item3);
        list1.add(itemA);
        list1.add(itemB);
        list1.add(itemC);
        list1.add(itemD);
        insert list1;
        
        // Seven items are now in the shipping invoice. 
       // The following deletes four of them.
        List<Item\_\_c> list2 = new List<Item\_\_c>();
        list2.add(itemA);
        list2.add(itemB);
        list2.add(itemC);
        list2.add(itemD);
        delete list2;
        
        // Retrieve the order and verify the deletion
        order1 = \[SELECT id, subtotal\_\_c, tax\_\_c, shipping\_\_c, totalweight\_\_c, 
                  grandtotal\_\_c, shippingdiscount\_\_c 
                  FROM Shipping\_Invoice\_\_C 
                  WHERE Id = :order1.Id WITH USER\_MODE\];
        
        Assert.areEqual(75, order1.subtotal\_\_c, 
                      'Order subtotal was not $75, but was '\+ order1.subtotal\_\_c);
        Assert.areEqual(6.9375, order1.tax\_\_c, 
                      'Order tax was not $6.9375, but was ' + order1.tax\_\_c);
        Assert.areEqual(4.50, order1.shipping\_\_c, 
                      'Order shipping was not $4.50, but was ' + order1.shipping\_\_c);
        Assert.areEqual(6.00, order1.totalweight\_\_c, 
                      'Order weight was not 6 but was ' + order1.totalweight\_\_c);
        Assert.areEqual(86.4375, order1.grandtotal\_\_c, 
                      'Order grand total was not $86.4375 but was ' 
                      + order1.grandtotal\_\_c);
        Assert.areEqual(0, order1.shippingdiscount\_\_c, 
                      'Order shipping discount was not $0 but was ' 
                      + order1.shippingdiscount\_\_c);
    }
    // Testing free shipping
    @IsTest
    public static void testFreeShipping(){

        // Create the shipping invoice. It's a best practice to either use defaults
        // or to explicitly set all values to zero so as to avoid having
        // extraneous data in your test.
        Shipping\_Invoice\_\_C order1 = new Shipping\_Invoice\_\_C(subtotal\_\_c = 0, 
                          totalweight\_\_c = 0, grandtotal\_\_c = 0, 
                          ShippingDiscount\_\_c = 0, Shipping\_\_c = 0, tax\_\_c = 0);

        // Insert the order and populate with items.
        insert as user Order1;
        List<Item\_\_c> list1 = new List<Item\_\_c>();
        Item\_\_c item1 = new Item\_\_C(Price\_\_c = 10, weight\_\_c = 1, 
                                 quantity\_\_c = 1, Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c item2 = new Item\_\_C(Price\_\_c = 25, weight\_\_c = 2, 
                                 quantity\_\_c = 1, Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c item3 = new Item\_\_C(Price\_\_c = 40, weight\_\_c = 3, 
                                 quantity\_\_c = 1, Shipping\_Invoice\_\_C = order1.id);
        list1.add(item1);
        list1.add(item2);
        list1.add(item3);
        insert as user list1;
        
        // Retrieve the order and verify free shipping not applicable
        order1 = \[SELECT id, subtotal\_\_c, tax\_\_c, shipping\_\_c, totalweight\_\_c, 
                  grandtotal\_\_c, shippingdiscount\_\_c 
                  FROM Shipping\_Invoice\_\_C 
                  WHERE Id = :order1.Id WITH USER\_MODE\];
        
        // Free shipping not available on $75 orders
        Assert.areEqual(75, order1.subtotal\_\_c, 
                      'Order subtotal was not $75, but was '\+ order1.subtotal\_\_c);
        Assert.areEqual(6.9375, order1.tax\_\_c, 
                      'Order tax was not $6.9375, but was ' + order1.tax\_\_c);
        Assert.areEqual(4.50, order1.shipping\_\_c, 
                      'Order shipping was not $4.50, but was ' + order1.shipping\_\_c);
        Assert.areEqual(6.00, order1.totalweight\_\_c, 
                      'Order weight was not 6 but was ' + order1.totalweight\_\_c);
        Assert.areEqual(86.4375, order1.grandtotal\_\_c, 
                      'Order grand total was not $86.4375 but was ' 
                      + order1.grandtotal\_\_c);
        Assert.areEqual(0, order1.shippingdiscount\_\_c, 
                      'Order shipping discount was not $0 but was ' 
                      + order1.shippingdiscount\_\_c);
        
        // Add items to increase subtotal
        item1 = new Item\_\_C(Price\_\_c = 25, weight\_\_c = 20, quantity\_\_c = 1, 
                            Shipping\_Invoice\_\_C = order1.id);       
        insert as user item1;

        // Retrieve the order and verify free shipping is applicable
        order1 = \[SELECT id, subtotal\_\_c, tax\_\_c, shipping\_\_c, totalweight\_\_c, 
                  grandtotal\_\_c, shippingdiscount\_\_c 
                  FROM Shipping\_Invoice\_\_C 
                  WHERE Id = :order1.Id WITH USER\_MODE\];
        
        // Order total is now at $100, so free shipping should be enabled
        Assert.areEqual(100, order1.subtotal\_\_c, 
                      'Order subtotal was not $100, but was '\+ order1.subtotal\_\_c);
        Assert.areEqual(9.25, order1.tax\_\_c, 
                      'Order tax was not $9.25, but was ' + order1.tax\_\_c);
        Assert.areEqual(19.50, order1.shipping\_\_c, 
                      'Order shipping was not $19.50, but was ' 
                      + order1.shipping\_\_c);
        Assert.areEqual(26.00, order1.totalweight\_\_c, 
                      'Order weight was not 26 but was ' + order1.totalweight\_\_c);
        Assert.areEqual(109.25, order1.grandtotal\_\_c, 
                      'Order grand total was not $86.4375 but was ' 
                      + order1.grandtotal\_\_c);
        Assert.areEqual(-19.50, order1.shippingdiscount\_\_c, 
                      'Order shipping discount was not -$19.50 but was ' 
                      + order1.shippingdiscount\_\_c);
    }
    
     // Negative testing for inserting bad input
    @IsTest
    public static void testNegativeTests(){

        // Create the shipping invoice. It's a best practice to either use defaults
        // or to explicitly set all values to zero so as to avoid having
        // extraneous data in your test.
        Shipping\_Invoice\_\_C order1 = new Shipping\_Invoice\_\_C(subtotal\_\_c = 0, 
                          totalweight\_\_c = 0, grandtotal\_\_c = 0, 
                          ShippingDiscount\_\_c = 0, Shipping\_\_c = 0, tax\_\_c = 0);

        // Insert the order and populate with items. 
        insert Order1;
        Item\_\_c item1 = new Item\_\_C(Price\_\_c = -10, weight\_\_c = 1, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c item2 = new Item\_\_C(Price\_\_c = 25, weight\_\_c = -2, quantity\_\_c = 1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c item3 = new Item\_\_C(Price\_\_c = 40, weight\_\_c = 3, quantity\_\_c = -1, 
                                    Shipping\_Invoice\_\_C = order1.id);
        Item\_\_c item4 = new Item\_\_C(Price\_\_c = 40, weight\_\_c = 3, quantity\_\_c = 0, 
                                    Shipping\_Invoice\_\_C = order1.id);

        try{
            insert item1;
        }
        catch(Exception e)
        {
            Assert.isTrue(e.getMessage().contains('Price must be non-negative'), 
                         'Price was negative but was not caught');
        }
        
        try{
            insert item2;
        }
        catch(Exception e)
        {
            Assert.isTrue(e.getMessage().contains('Weight must be non-negative'), 
                         'Weight was negative but was not caught');
        }

        try{
            insert item3;
        }
        catch(Exception e)
        {
            Assert.isTrue(e.getMessage().contains('Quantity must be positive'), 
                         'Quantity was negative but was not caught');
        }
        
        try{
            insert item4;
        }
        catch(Exception e)
        {
            Assert.isTrue(e.getMessage().contains('Quantity must be positive'), 
                         'Quantity was zero but was not caught');
        }
    }
}

- [← Previous](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_shopping_cart_example_walkthru.htm "Shipping Invoice Example Walk-Through")