Newer Version Available

This content describes an older version of this product. View Latest

Shipping Invoice Example Code

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

Calculate Trigger

1trigger calculate on Item__c (after insert, after update, after delete) {
2
3// Use a map because it doesn't allow duplicate values
4
5Map<ID, Shipping_Invoice__C> updateMap = new Map<ID, Shipping_Invoice__C>();
6
7// Set this integer to -1 if we are deleting
8Integer subtract ;
9
10// Populate the list of items based on trigger type
11List<Item__c> itemList;
12    if(trigger.isInsert || trigger.isUpdate){
13        itemList = Trigger.new;
14        subtract = 1;
15    }
16    else if(trigger.isDelete)
17    {
18        // Note -- there is no trigger.new in delete
19        itemList = trigger.old;
20        subtract = -1;
21    }
22
23// Access all the information we need in a single query 
24// rather than querying when we need it.
25// This is a best practice for bulkifying requests
26
27set<Id> AllItems = new set<id>();
28
29for(item__c i :itemList){
30// Assert numbers are not negative.  
31// None of the fields would make sense with a negative value
32
33System.assert(i.quantity__c > 0, 'Quantity must be positive');
34System.assert(i.weight__c >= 0, 'Weight must be non-negative');
35System.assert(i.price__c >= 0, 'Price must be non-negative');
36
37// If there is a duplicate Id, it won't get added to a set
38AllItems.add(i.Shipping_Invoice__C);
39}
40
41// Accessing all shipping invoices associated with the items in the trigger
42List<Shipping_Invoice__C> AllShippingInvoices = [SELECT Id, ShippingDiscount__c, 
43                   SubTotal__c, TotalWeight__c, Tax__c, GrandTotal__c 
44                   FROM Shipping_Invoice__C WHERE Id IN :AllItems];
45                   
46// Take the list we just populated and put it into a Map.  
47// This will make it easier to look up a shipping invoice
48// because you must iterate a list, but you can use lookup for a map, 
49Map<ID, Shipping_Invoice__C> SIMap = new Map<ID, Shipping_Invoice__C>();
50
51for(Shipping_Invoice__C sc : AllShippingInvoices)
52{
53    SIMap.put(sc.id, sc);
54}
55
56// Process the list of items
57    if(Trigger.isUpdate)
58    {
59        // Treat updates like a removal of the old item and addition of the         
60        // revised item rather than figuring out the differences of each field 
61        // and acting accordingly.
62        // Note updates have both trigger.new and trigger.old
63        for(Integer x = 0; x < Trigger.old.size(); x++)
64        {
65            Shipping_Invoice__C myOrder;
66            myOrder = SIMap.get(trigger.old[x].Shipping_Invoice__C);
67
68            // Decrement the previous value from the subtotal and weight.
69            myOrder.SubTotal__c -= (trigger.old[x].price__c * 
70                                    trigger.old[x].quantity__c);
71            myOrder.TotalWeight__c -= (trigger.old[x].weight__c * 
72                                       trigger.old[x].quantity__c);
73                
74            // Increment the new subtotal and weight.
75            myOrder.SubTotal__c += (trigger.new[x].price__c * 
76                                    trigger.new[x].quantity__c);
77            myOrder.TotalWeight__c += (trigger.new[x].weight__c * 
78                                       trigger.new[x].quantity__c);
79        }
80        
81        for(Shipping_Invoice__C myOrder : AllShippingInvoices)
82        {
83            
84            // Set tax rate to 9.25%  Please note, this is a simple example.  
85            // Generally, you would never hard code values.
86            // Leveraging Custom Settings for tax rates is a best practice.  
87            // See Custom Settings in the Apex Developer Guide 
88            // for more information.
89            myOrder.Tax__c = myOrder.Subtotal__c * .0925;
90            
91            // Reset the shipping discount
92            myOrder.ShippingDiscount__c = 0;
93    
94            // Set shipping rate to 75 cents per pound.  
95            // Generally, you would never hard code values.
96            // Leveraging Custom Settings for the shipping rate is a best practice.
97            // See Custom Settings in the Apex Developer Guide 
98            // for more information.
99            myOrder.Shipping__c = (myOrder.totalWeight__c * .75);
100            myOrder.GrandTotal__c = myOrder.SubTotal__c + myOrder.tax__c + 
101                                    myOrder.Shipping__c;
102            updateMap.put(myOrder.id, myOrder);
103         }
104    }
105    else
106    { 
107        for(Item__c itemToProcess : itemList)
108        {
109            Shipping_Invoice__C myOrder;
110    
111            // Look up the correct shipping invoice from the ones we got earlier
112            myOrder = SIMap.get(itemToProcess.Shipping_Invoice__C);
113            myOrder.SubTotal__c += (itemToProcess.price__c * 
114                                    itemToProcess.quantity__c * subtract);
115            myOrder.TotalWeight__c += (itemToProcess.weight__c * 
116                                       itemToProcess.quantity__c * subtract);
117        }
118        
119        for(Shipping_Invoice__C myOrder : AllShippingInvoices)
120        {
121            
122             // Set tax rate to 9.25%  Please note, this is a simple example.  
123             // Generally, you would never hard code values.
124             // Leveraging Custom Settings for tax rates is a best practice.  
125             // See Custom Settings in the Apex Developer Guide 
126             // for more information.
127             myOrder.Tax__c = myOrder.Subtotal__c * .0925;
128             
129             // Reset shipping discount
130             myOrder.ShippingDiscount__c = 0;
131    
132            // Set shipping rate to 75 cents per pound.  
133            // Generally, you would never hard code values.
134            // Leveraging Custom Settings for the shipping rate is a best practice.
135            // See Custom Settings in the Apex Developer Guide 
136            // for more information.
137            myOrder.Shipping__c = (myOrder.totalWeight__c * .75);
138            myOrder.GrandTotal__c = myOrder.SubTotal__c + myOrder.tax__c + 
139                                    myOrder.Shipping__c;
140                                       
141            updateMap.put(myOrder.id, myOrder);
142    
143         }
144     }    
145     
146     // Only use one DML update at the end.
147     // This minimizes the number of DML requests generated from this trigger.
148     update updateMap.values();
149}

ShippingDiscount Trigger

1trigger ShippingDiscount on Shipping_Invoice__C (before update) {
2    // Free shipping on all orders greater than $100
3    
4    for(Shipping_Invoice__C myShippingInvoice : Trigger.new)
5    {
6        if((myShippingInvoice.subtotal__c >= 100.00) && 
7           (myShippingInvoice.ShippingDiscount__c == 0))
8        {
9            myShippingInvoice.ShippingDiscount__c = 
10                         myShippingInvoice.Shipping__c * -1;
11            myShippingInvoice.GrandTotal__c += myShippingInvoice.ShippingDiscount__c;
12        }
13    }
14}

Shipping Invoice Test

1@IsTest
2private class TestShippingInvoice{
3
4    // Test for inserting three items at once
5    public static testmethod void testBulkItemInsert(){
6        // Create the shipping invoice. It's a best practice to either use defaults
7        // or to explicitly set all values to zero so as to avoid having
8        // extraneous data in your test.
9        Shipping_Invoice__C order1 = new Shipping_Invoice__C(subtotal__c = 0, 
10                          totalweight__c = 0, grandtotal__c = 0, 
11                          ShippingDiscount__c = 0, Shipping__c = 0, tax__c = 0);
12
13        // Insert the order and populate with items
14        insert Order1;
15        List<Item__c> list1 = new List<Item__c>();
16        Item__c item1 = new Item__C(Price__c = 10, weight__c = 1, quantity__c = 1, 
17                                    Shipping_Invoice__C = order1.id);
18        Item__c item2 = new Item__C(Price__c = 25, weight__c = 2, quantity__c = 1, 
19                                    Shipping_Invoice__C = order1.id);
20        Item__c item3 = new Item__C(Price__c = 40, weight__c = 3, quantity__c = 1, 
21                                    Shipping_Invoice__C = order1.id);
22        list1.add(item1);
23        list1.add(item2);
24        list1.add(item3);
25        insert list1;
26        
27        // Retrieve the order, then do assertions
28        order1 = [SELECT id, subtotal__c, tax__c, shipping__c, totalweight__c, 
29                  grandtotal__c, shippingdiscount__c 
30                  FROM Shipping_Invoice__C 
31                  WHERE id = :order1.id];
32        
33        System.assert(order1.subtotal__c == 75, 
34                'Order subtotal was not $75, but was '+ order1.subtotal__c);
35        System.assert(order1.tax__c == 6.9375, 
36                'Order tax was not $6.9375, but was ' + order1.tax__c);
37        System.assert(order1.shipping__c == 4.50, 
38                'Order shipping was not $4.50, but was ' + order1.shipping__c);
39        System.assert(order1.totalweight__c == 6.00, 
40                'Order weight was not 6 but was ' + order1.totalweight__c);
41        System.assert(order1.grandtotal__c == 86.4375, 
42                'Order grand total was not $86.4375 but was ' 
43                 + order1.grandtotal__c);
44        System.assert(order1.shippingdiscount__c == 0, 
45                'Order shipping discount was not $0 but was ' 
46                + order1.shippingdiscount__c);
47    }
48    
49    // Test for updating three items at once
50    public static testmethod void testBulkItemUpdate(){
51
52        // Create the shipping invoice. It's a best practice to either use defaults
53        // or to explicitly set all values to zero so as to avoid having
54        // extraneous data in your test.
55        Shipping_Invoice__C order1 = new Shipping_Invoice__C(subtotal__c = 0, 
56                          totalweight__c = 0, grandtotal__c = 0, 
57                          ShippingDiscount__c = 0, Shipping__c = 0, tax__c = 0);
58
59        // Insert the order and populate with items.
60        insert Order1;
61        List<Item__c> list1 = new List<Item__c>();
62        Item__c item1 = new Item__C(Price__c = 1, weight__c = 1, quantity__c = 1, 
63                                    Shipping_Invoice__C = order1.id);
64        Item__c item2 = new Item__C(Price__c = 2, weight__c = 2, quantity__c = 1, 
65                                    Shipping_Invoice__C = order1.id);
66        Item__c item3 = new Item__C(Price__c = 4, weight__c = 3, quantity__c = 1, 
67                                    Shipping_Invoice__C = order1.id);
68        list1.add(item1);
69        list1.add(item2);
70        list1.add(item3);
71        insert list1;
72        
73        // Update the prices on the 3 items
74        list1[0].price__c = 10;
75        list1[1].price__c = 25;
76        list1[2].price__c = 40;
77        update list1;
78        
79        // Access the order and assert items updated
80        order1 = [SELECT id, subtotal__c, tax__c, shipping__c, totalweight__c, 
81                  grandtotal__c, shippingdiscount__c 
82                  FROM Shipping_Invoice__C 
83                  WHERE Id = :order1.Id];
84
85        System.assert(order1.subtotal__c == 75, 
86                       'Order subtotal was not $75, but was '+ order1.subtotal__c);
87        System.assert(order1.tax__c == 6.9375, 
88                       'Order tax was not $6.9375, but was ' + order1.tax__c);
89        System.assert(order1.shipping__c == 4.50, 
90                       'Order shipping was not $4.50, but was ' 
91                       + order1.shipping__c);
92        System.assert(order1.totalweight__c == 6.00, 
93                       'Order weight was not 6 but was ' + order1.totalweight__c);
94        System.assert(order1.grandtotal__c == 86.4375, 
95                       'Order grand total was not $86.4375 but was ' 
96                       + order1.grandtotal__c);
97        System.assert(order1.shippingdiscount__c == 0, 
98                       'Order shipping discount was not $0 but was ' 
99                       + order1.shippingdiscount__c);
100    
101    }
102    
103    // Test for deleting items
104    public static testmethod void testBulkItemDelete(){
105
106        // Create the shipping invoice. It's a best practice to either use defaults
107        // or to explicitly set all values to zero so as to avoid having
108        // extraneous data in your test.
109        Shipping_Invoice__C order1 = new Shipping_Invoice__C(subtotal__c = 0, 
110                          totalweight__c = 0, grandtotal__c = 0, 
111                          ShippingDiscount__c = 0, Shipping__c = 0, tax__c = 0);
112
113        // Insert the order and populate with items
114        insert Order1;
115        List<Item__c> list1 = new List<Item__c>();
116        Item__c item1 = new Item__C(Price__c = 10, weight__c = 1, quantity__c = 1, 
117                                    Shipping_Invoice__C = order1.id);
118        Item__c item2 = new Item__C(Price__c = 25, weight__c = 2, quantity__c = 1, 
119                                    Shipping_Invoice__C = order1.id);
120        Item__c item3 = new Item__C(Price__c = 40, weight__c = 3, quantity__c = 1, 
121                                    Shipping_Invoice__C = order1.id);
122        Item__c itemA = new Item__C(Price__c = 1, weight__c = 3, quantity__c = 1, 
123                                    Shipping_Invoice__C = order1.id);
124        Item__c itemB = new Item__C(Price__c = 1, weight__c = 3, quantity__c = 1, 
125                                    Shipping_Invoice__C = order1.id);
126        Item__c itemC = new Item__C(Price__c = 1, weight__c = 3, quantity__c = 1, 
127                                    Shipping_Invoice__C = order1.id);
128        Item__c itemD = new Item__C(Price__c = 1, weight__c = 3, quantity__c = 1, 
129                                    Shipping_Invoice__C = order1.id);
130        list1.add(item1);
131        list1.add(item2);
132        list1.add(item3);
133        list1.add(itemA);
134        list1.add(itemB);
135        list1.add(itemC);
136        list1.add(itemD);
137        insert list1;
138        
139        // Seven items are now in the shipping invoice. 
140       // The following deletes four of them.
141        List<Item__c> list2 = new List<Item__c>();
142        list2.add(itemA);
143        list2.add(itemB);
144        list2.add(itemC);
145        list2.add(itemD);
146        delete list2;
147        
148        // Retrieve the order and verify the deletion
149        order1 = [SELECT id, subtotal__c, tax__c, shipping__c, totalweight__c, 
150                  grandtotal__c, shippingdiscount__c 
151                  FROM Shipping_Invoice__C 
152                  WHERE Id = :order1.Id];
153        
154        System.assert(order1.subtotal__c == 75, 
155                      'Order subtotal was not $75, but was '+ order1.subtotal__c);
156        System.assert(order1.tax__c == 6.9375, 
157                      'Order tax was not $6.9375, but was ' + order1.tax__c);
158        System.assert(order1.shipping__c == 4.50, 
159                      'Order shipping was not $4.50, but was ' + order1.shipping__c);
160        System.assert(order1.totalweight__c == 6.00, 
161                      'Order weight was not 6 but was ' + order1.totalweight__c);
162        System.assert(order1.grandtotal__c == 86.4375, 
163                      'Order grand total was not $86.4375 but was ' 
164                      + order1.grandtotal__c);
165        System.assert(order1.shippingdiscount__c == 0, 
166                      'Order shipping discount was not $0 but was ' 
167                      + order1.shippingdiscount__c);
168    }
169    // Testing free shipping
170    public static testmethod void testFreeShipping(){
171
172        // Create the shipping invoice. It's a best practice to either use defaults
173        // or to explicitly set all values to zero so as to avoid having
174        // extraneous data in your test.
175        Shipping_Invoice__C order1 = new Shipping_Invoice__C(subtotal__c = 0, 
176                          totalweight__c = 0, grandtotal__c = 0, 
177                          ShippingDiscount__c = 0, Shipping__c = 0, tax__c = 0);
178
179        // Insert the order and populate with items.
180        insert Order1;
181        List<Item__c> list1 = new List<Item__c>();
182        Item__c item1 = new Item__C(Price__c = 10, weight__c = 1, 
183                                 quantity__c = 1, Shipping_Invoice__C = order1.id);
184        Item__c item2 = new Item__C(Price__c = 25, weight__c = 2, 
185                                 quantity__c = 1, Shipping_Invoice__C = order1.id);
186        Item__c item3 = new Item__C(Price__c = 40, weight__c = 3, 
187                                 quantity__c = 1, Shipping_Invoice__C = order1.id);
188        list1.add(item1);
189        list1.add(item2);
190        list1.add(item3);
191        insert list1;
192        
193        // Retrieve the order and verify free shipping not applicable
194        order1 = [SELECT id, subtotal__c, tax__c, shipping__c, totalweight__c, 
195                  grandtotal__c, shippingdiscount__c 
196                  FROM Shipping_Invoice__C 
197                  WHERE Id = :order1.Id];
198        
199        // Free shipping not available on $75 orders
200        System.assert(order1.subtotal__c == 75, 
201                      'Order subtotal was not $75, but was '+ order1.subtotal__c);
202        System.assert(order1.tax__c == 6.9375, 
203                      'Order tax was not $6.9375, but was ' + order1.tax__c);
204        System.assert(order1.shipping__c == 4.50, 
205                      'Order shipping was not $4.50, but was ' + order1.shipping__c);
206        System.assert(order1.totalweight__c == 6.00, 
207                      'Order weight was not 6 but was ' + order1.totalweight__c);
208        System.assert(order1.grandtotal__c == 86.4375, 
209                      'Order grand total was not $86.4375 but was ' 
210                      + order1.grandtotal__c);
211        System.assert(order1.shippingdiscount__c == 0, 
212                      'Order shipping discount was not $0 but was ' 
213                      + order1.shippingdiscount__c);
214        
215        // Add items to increase subtotal
216        item1 = new Item__C(Price__c = 25, weight__c = 20, quantity__c = 1, 
217                            Shipping_Invoice__C = order1.id);       
218        insert item1;
219
220        // Retrieve the order and verify free shipping is applicable
221        order1 = [SELECT id, subtotal__c, tax__c, shipping__c, totalweight__c, 
222                  grandtotal__c, shippingdiscount__c 
223                  FROM Shipping_Invoice__C 
224                  WHERE Id = :order1.Id];
225        
226        // Order total is now at $100, so free shipping should be enabled
227        System.assert(order1.subtotal__c == 100, 
228                      'Order subtotal was not $100, but was '+ order1.subtotal__c);
229        System.assert(order1.tax__c == 9.25, 
230                      'Order tax was not $9.25, but was ' + order1.tax__c);
231        System.assert(order1.shipping__c == 19.50, 
232                      'Order shipping was not $19.50, but was ' 
233                      + order1.shipping__c);
234        System.assert(order1.totalweight__c == 26.00, 
235                      'Order weight was not 26 but was ' + order1.totalweight__c);
236        System.assert(order1.grandtotal__c == 109.25, 
237                      'Order grand total was not $86.4375 but was ' 
238                      + order1.grandtotal__c);
239        System.assert(order1.shippingdiscount__c == -19.50, 
240                      'Order shipping discount was not -$19.50 but was ' 
241                      + order1.shippingdiscount__c);
242    }
243    
244     // Negative testing for inserting bad input
245    public static testmethod void testNegativeTests(){
246
247        // Create the shipping invoice. It's a best practice to either use defaults
248        // or to explicitly set all values to zero so as to avoid having
249        // extraneous data in your test.
250        Shipping_Invoice__C order1 = new Shipping_Invoice__C(subtotal__c = 0, 
251                          totalweight__c = 0, grandtotal__c = 0, 
252                          ShippingDiscount__c = 0, Shipping__c = 0, tax__c = 0);
253
254        // Insert the order and populate with items. 
255        insert Order1;
256        Item__c item1 = new Item__C(Price__c = -10, weight__c = 1, quantity__c = 1, 
257                                    Shipping_Invoice__C = order1.id);
258        Item__c item2 = new Item__C(Price__c = 25, weight__c = -2, quantity__c = 1, 
259                                    Shipping_Invoice__C = order1.id);
260        Item__c item3 = new Item__C(Price__c = 40, weight__c = 3, quantity__c = -1, 
261                                    Shipping_Invoice__C = order1.id);
262        Item__c item4 = new Item__C(Price__c = 40, weight__c = 3, quantity__c = 0, 
263                                    Shipping_Invoice__C = order1.id);
264
265        try{
266            insert item1;
267        }
268        catch(Exception e)
269        {
270            system.assert(e.getMessage().contains('Price must be non-negative'), 
271                         'Price was negative but was not caught');
272        }
273        
274        try{
275            insert item2;
276        }
277        catch(Exception e)
278        {
279            system.assert(e.getMessage().contains('Weight must be non-negative'), 
280                         'Weight was negative but was not caught');
281        }
282
283        try{
284            insert item3;
285        }
286        catch(Exception e)
287        {
288            system.assert(e.getMessage().contains('Quantity must be positive'), 
289                         'Quantity was negative but was not caught');
290        }
291        
292        try{
293            insert item4;
294        }
295        catch(Exception e)
296        {
297            system.assert(e.getMessage().contains('Quantity must be positive'), 
298                         'Quantity was zero but was not caught');
299        }
300    }
301}