Newer Version Available
Shipping Invoice Example Code
The following triggers and test class make up the shipping invoice example application:
Calculate Trigger
1swfobject.registerObject("clippy.codeblock-0", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17trigger calculate on Item__c (after insert, after update, after delete) {
18
19// Use a map because it doesn't allow duplicate values
20
21Map<ID, Shipping_Invoice__C> updateMap = new Map<ID, Shipping_Invoice__C>();
22
23// Set this integer to -1 if we are deleting
24Integer subtract ;
25
26// Populate the list of items based on trigger type
27List<Item__c> itemList;
28 if(trigger.isInsert || trigger.isUpdate){
29 itemList = Trigger.new;
30 subtract = 1;
31 }
32 else if(trigger.isDelete)
33 {
34 // Note -- there is no trigger.new in delete
35 itemList = trigger.old;
36 subtract = -1;
37 }
38
39// Access all the information we need in a single query
40// rather than querying when we need it.
41// This is a best practice for bulkifying requests
42
43set<Id> AllItems = new set<id>();
44
45for(item__c i :itemList){
46// Assert numbers are not negative.
47// None of the fields would make sense with a negative value
48
49System.assert(i.quantity__c > 0, 'Quantity must be positive');
50System.assert(i.weight__c >= 0, 'Weight must be non-negative');
51System.assert(i.price__c >= 0, 'Price must be non-negative');
52
53// If there is a duplicate Id, it won't get added to a set
54AllItems.add(i.Shipping_Invoice__C);
55}
56
57// Accessing all shipping invoices associated with the items in the trigger
58List<Shipping_Invoice__C> AllShippingInvoices = [SELECT Id, ShippingDiscount__c,
59 SubTotal__c, TotalWeight__c, Tax__c, GrandTotal__c
60 FROM Shipping_Invoice__C WHERE Id IN :AllItems];
61
62// Take the list we just populated and put it into a Map.
63// This will make it easier to look up a shipping invoice
64// because you must iterate a list, but you can use lookup for a map,
65Map<ID, Shipping_Invoice__C> SIMap = new Map<ID, Shipping_Invoice__C>();
66
67for(Shipping_Invoice__C sc : AllShippingInvoices)
68{
69 SIMap.put(sc.id, sc);
70}
71
72// Process the list of items
73 if(Trigger.isUpdate)
74 {
75 // Treat updates like a removal of the old item and addition of the
76 // revised item rather than figuring out the differences of each field
77 // and acting accordingly.
78 // Note updates have both trigger.new and trigger.old
79 for(Integer x = 0; x < Trigger.old.size(); x++)
80 {
81 Shipping_Invoice__C myOrder;
82 myOrder = SIMap.get(trigger.old[x].Shipping_Invoice__C);
83
84 // Decrement the previous value from the subtotal and weight.
85 myOrder.SubTotal__c -= (trigger.old[x].price__c *
86 trigger.old[x].quantity__c);
87 myOrder.TotalWeight__c -= (trigger.old[x].weight__c *
88 trigger.old[x].quantity__c);
89
90 // Increment the new subtotal and weight.
91 myOrder.SubTotal__c += (trigger.new[x].price__c *
92 trigger.new[x].quantity__c);
93 myOrder.TotalWeight__c += (trigger.new[x].weight__c *
94 trigger.new[x].quantity__c);
95 }
96
97 for(Shipping_Invoice__C myOrder : AllShippingInvoices)
98 {
99
100 // Set tax rate to 9.25% Please note, this is a simple example.
101 // Generally, you would never hard code values.
102 // Leveraging Custom Settings for tax rates is a best practice.
103 // See Custom Settings in the Apex Developer's guide
104 // for more information.
105 myOrder.Tax__c = myOrder.Subtotal__c * .0925;
106
107 // Reset the shipping discount
108 myOrder.ShippingDiscount__c = 0;
109
110 // Set shipping rate to 75 cents per pound.
111 // Generally, you would never hard code values.
112 // Leveraging Custom Settings for the shipping rate is a best practice.
113 // See Custom Settings in the Apex Developer's guide
114 // for more information.
115 myOrder.Shipping__c = (myOrder.totalWeight__c * .75);
116 myOrder.GrandTotal__c = myOrder.SubTotal__c + myOrder.tax__c +
117 myOrder.Shipping__c;
118 updateMap.put(myOrder.id, myOrder);
119 }
120 }
121 else
122 {
123 for(Item__c itemToProcess : itemList)
124 {
125 Shipping_Invoice__C myOrder;
126
127 // Look up the correct shipping invoice from the ones we got earlier
128 myOrder = SIMap.get(itemToProcess.Shipping_Invoice__C);
129 myOrder.SubTotal__c += (itemToProcess.price__c *
130 itemToProcess.quantity__c * subtract);
131 myOrder.TotalWeight__c += (itemToProcess.weight__c *
132 itemToProcess.quantity__c * subtract);
133 }
134
135 for(Shipping_Invoice__C myOrder : AllShippingInvoices)
136 {
137
138 // Set tax rate to 9.25% Please note, this is a simple example.
139 // Generally, you would never hard code values.
140 // Leveraging Custom Settings for tax rates is a best practice.
141 // See Custom Settings in the Apex Developer's guide
142 // for more information.
143 myOrder.Tax__c = myOrder.Subtotal__c * .0925;
144
145 // Reset shipping discount
146 myOrder.ShippingDiscount__c = 0;
147
148 // Set shipping rate to 75 cents per pound.
149 // Generally, you would never hard code values.
150 // Leveraging Custom Settings for the shipping rate is a best practice.
151 // See Custom Settings in the Apex Developer's guide
152 // for more information.
153 myOrder.Shipping__c = (myOrder.totalWeight__c * .75);
154 myOrder.GrandTotal__c = myOrder.SubTotal__c + myOrder.tax__c +
155 myOrder.Shipping__c;
156
157 updateMap.put(myOrder.id, myOrder);
158
159 }
160 }
161
162 // Only use one DML update at the end.
163 // This minimizes the number of DML requests generated from this trigger.
164 update updateMap.values();
165}
166ShippingDiscount Trigger
1swfobject.registerObject("clippy.codeblock-1", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17trigger ShippingDiscount on Shipping_Invoice__C (before update) {
18 // Free shipping on all orders greater than $100
19
20 for(Shipping_Invoice__C myShippingInvoice : Trigger.new)
21 {
22 if((myShippingInvoice.subtotal__c >= 100.00) &&
23 (myShippingInvoice.ShippingDiscount__c == 0))
24 {
25 myShippingInvoice.ShippingDiscount__c =
26 myShippingInvoice.Shipping__c * -1;
27 myShippingInvoice.GrandTotal__c += myShippingInvoice.ShippingDiscount__c;
28 }
29 }
30}
31Shipping Invoice Test
1swfobject.registerObject("clippy.codeblock-2", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@IsTest
18private class TestShippingInvoice{
19
20 // Test for inserting three items at once
21 public static testmethod void testBulkItemInsert(){
22 // Create the shipping invoice. It's a best practice to either use defaults
23 // or to explicitly set all values to zero so as to avoid having
24 // extraneous data in your test.
25 Shipping_Invoice__C order1 = new Shipping_Invoice__C(subtotal__c = 0,
26 totalweight__c = 0, grandtotal__c = 0,
27 ShippingDiscount__c = 0, Shipping__c = 0, tax__c = 0);
28
29 // Insert the order and populate with items
30 insert Order1;
31 List<Item__c> list1 = new List<Item__c>();
32 Item__c item1 = new Item__C(Price__c = 10, weight__c = 1, quantity__c = 1,
33 Shipping_Invoice__C = order1.id);
34 Item__c item2 = new Item__C(Price__c = 25, weight__c = 2, quantity__c = 1,
35 Shipping_Invoice__C = order1.id);
36 Item__c item3 = new Item__C(Price__c = 40, weight__c = 3, quantity__c = 1,
37 Shipping_Invoice__C = order1.id);
38 list1.add(item1);
39 list1.add(item2);
40 list1.add(item3);
41 insert list1;
42
43 // Retrieve the order, then do assertions
44 order1 = [SELECT id, subtotal__c, tax__c, shipping__c, totalweight__c,
45 grandtotal__c, shippingdiscount__c
46 FROM Shipping_Invoice__C
47 WHERE id = :order1.id];
48
49 System.assert(order1.subtotal__c == 75,
50 'Order subtotal was not $75, but was '+ order1.subtotal__c);
51 System.assert(order1.tax__c == 6.9375,
52 'Order tax was not $6.9375, but was ' + order1.tax__c);
53 System.assert(order1.shipping__c == 4.50,
54 'Order shipping was not $4.50, but was ' + order1.shipping__c);
55 System.assert(order1.totalweight__c == 6.00,
56 'Order weight was not 6 but was ' + order1.totalweight__c);
57 System.assert(order1.grandtotal__c == 86.4375,
58 'Order grand total was not $86.4375 but was '
59 + order1.grandtotal__c);
60 System.assert(order1.shippingdiscount__c == 0,
61 'Order shipping discount was not $0 but was '
62 + order1.shippingdiscount__c);
63 }
64
65 // Test for updating three items at once
66 public static testmethod void testBulkItemUpdate(){
67
68 // Create the shipping invoice. It's a best practice to either use defaults
69 // or to explicitly set all values to zero so as to avoid having
70 // extraneous data in your test.
71 Shipping_Invoice__C order1 = new Shipping_Invoice__C(subtotal__c = 0,
72 totalweight__c = 0, grandtotal__c = 0,
73 ShippingDiscount__c = 0, Shipping__c = 0, tax__c = 0);
74
75 // Insert the order and populate with items.
76 insert Order1;
77 List<Item__c> list1 = new List<Item__c>();
78 Item__c item1 = new Item__C(Price__c = 1, weight__c = 1, quantity__c = 1,
79 Shipping_Invoice__C = order1.id);
80 Item__c item2 = new Item__C(Price__c = 2, weight__c = 2, quantity__c = 1,
81 Shipping_Invoice__C = order1.id);
82 Item__c item3 = new Item__C(Price__c = 4, weight__c = 3, quantity__c = 1,
83 Shipping_Invoice__C = order1.id);
84 list1.add(item1);
85 list1.add(item2);
86 list1.add(item3);
87 insert list1;
88
89 // Update the prices on the 3 items
90 list1[0].price__c = 10;
91 list1[1].price__c = 25;
92 list1[2].price__c = 40;
93 update list1;
94
95 // Access the order and assert items updated
96 order1 = [SELECT id, subtotal__c, tax__c, shipping__c, totalweight__c,
97 grandtotal__c, shippingdiscount__c
98 FROM Shipping_Invoice__C
99 WHERE Id = :order1.Id];
100
101 System.assert(order1.subtotal__c == 75,
102 'Order subtotal was not $75, but was '+ order1.subtotal__c);
103 System.assert(order1.tax__c == 6.9375,
104 'Order tax was not $6.9375, but was ' + order1.tax__c);
105 System.assert(order1.shipping__c == 4.50,
106 'Order shipping was not $4.50, but was '
107 + order1.shipping__c);
108 System.assert(order1.totalweight__c == 6.00,
109 'Order weight was not 6 but was ' + order1.totalweight__c);
110 System.assert(order1.grandtotal__c == 86.4375,
111 'Order grand total was not $86.4375 but was '
112 + order1.grandtotal__c);
113 System.assert(order1.shippingdiscount__c == 0,
114 'Order shipping discount was not $0 but was '
115 + order1.shippingdiscount__c);
116
117 }
118
119 // Test for deleting items
120 public static testmethod void testBulkItemDelete(){
121
122 // Create the shipping invoice. It's a best practice to either use defaults
123 // or to explicitly set all values to zero so as to avoid having
124 // extraneous data in your test.
125 Shipping_Invoice__C order1 = new Shipping_Invoice__C(subtotal__c = 0,
126 totalweight__c = 0, grandtotal__c = 0,
127 ShippingDiscount__c = 0, Shipping__c = 0, tax__c = 0);
128
129 // Insert the order and populate with items
130 insert Order1;
131 List<Item__c> list1 = new List<Item__c>();
132 Item__c item1 = new Item__C(Price__c = 10, weight__c = 1, quantity__c = 1,
133 Shipping_Invoice__C = order1.id);
134 Item__c item2 = new Item__C(Price__c = 25, weight__c = 2, quantity__c = 1,
135 Shipping_Invoice__C = order1.id);
136 Item__c item3 = new Item__C(Price__c = 40, weight__c = 3, quantity__c = 1,
137 Shipping_Invoice__C = order1.id);
138 Item__c itemA = new Item__C(Price__c = 1, weight__c = 3, quantity__c = 1,
139 Shipping_Invoice__C = order1.id);
140 Item__c itemB = new Item__C(Price__c = 1, weight__c = 3, quantity__c = 1,
141 Shipping_Invoice__C = order1.id);
142 Item__c itemC = new Item__C(Price__c = 1, weight__c = 3, quantity__c = 1,
143 Shipping_Invoice__C = order1.id);
144 Item__c itemD = new Item__C(Price__c = 1, weight__c = 3, quantity__c = 1,
145 Shipping_Invoice__C = order1.id);
146 list1.add(item1);
147 list1.add(item2);
148 list1.add(item3);
149 list1.add(itemA);
150 list1.add(itemB);
151 list1.add(itemC);
152 list1.add(itemD);
153 insert list1;
154
155 // Seven items are now in the shipping invoice.
156 // The following deletes four of them.
157 List<Item__c> list2 = new List<Item__c>();
158 list2.add(itemA);
159 list2.add(itemB);
160 list2.add(itemC);
161 list2.add(itemD);
162 delete list2;
163
164 // Retrieve the order and verify the deletion
165 order1 = [SELECT id, subtotal__c, tax__c, shipping__c, totalweight__c,
166 grandtotal__c, shippingdiscount__c
167 FROM Shipping_Invoice__C
168 WHERE Id = :order1.Id];
169
170 System.assert(order1.subtotal__c == 75,
171 'Order subtotal was not $75, but was '+ order1.subtotal__c);
172 System.assert(order1.tax__c == 6.9375,
173 'Order tax was not $6.9375, but was ' + order1.tax__c);
174 System.assert(order1.shipping__c == 4.50,
175 'Order shipping was not $4.50, but was ' + order1.shipping__c);
176 System.assert(order1.totalweight__c == 6.00,
177 'Order weight was not 6 but was ' + order1.totalweight__c);
178 System.assert(order1.grandtotal__c == 86.4375,
179 'Order grand total was not $86.4375 but was '
180 + order1.grandtotal__c);
181 System.assert(order1.shippingdiscount__c == 0,
182 'Order shipping discount was not $0 but was '
183 + order1.shippingdiscount__c);
184 }
185 // Testing free shipping
186 public static testmethod void testFreeShipping(){
187
188 // Create the shipping invoice. It's a best practice to either use defaults
189 // or to explicitly set all values to zero so as to avoid having
190 // extraneous data in your test.
191 Shipping_Invoice__C order1 = new Shipping_Invoice__C(subtotal__c = 0,
192 totalweight__c = 0, grandtotal__c = 0,
193 ShippingDiscount__c = 0, Shipping__c = 0, tax__c = 0);
194
195 // Insert the order and populate with items.
196 insert Order1;
197 List<Item__c> list1 = new List<Item__c>();
198 Item__c item1 = new Item__C(Price__c = 10, weight__c = 1,
199 quantity__c = 1, Shipping_Invoice__C = order1.id);
200 Item__c item2 = new Item__C(Price__c = 25, weight__c = 2,
201 quantity__c = 1, Shipping_Invoice__C = order1.id);
202 Item__c item3 = new Item__C(Price__c = 40, weight__c = 3,
203 quantity__c = 1, Shipping_Invoice__C = order1.id);
204 list1.add(item1);
205 list1.add(item2);
206 list1.add(item3);
207 insert list1;
208
209 // Retrieve the order and verify free shipping not applicable
210 order1 = [SELECT id, subtotal__c, tax__c, shipping__c, totalweight__c,
211 grandtotal__c, shippingdiscount__c
212 FROM Shipping_Invoice__C
213 WHERE Id = :order1.Id];
214
215 // Free shipping not available on $75 orders
216 System.assert(order1.subtotal__c == 75,
217 'Order subtotal was not $75, but was '+ order1.subtotal__c);
218 System.assert(order1.tax__c == 6.9375,
219 'Order tax was not $6.9375, but was ' + order1.tax__c);
220 System.assert(order1.shipping__c == 4.50,
221 'Order shipping was not $4.50, but was ' + order1.shipping__c);
222 System.assert(order1.totalweight__c == 6.00,
223 'Order weight was not 6 but was ' + order1.totalweight__c);
224 System.assert(order1.grandtotal__c == 86.4375,
225 'Order grand total was not $86.4375 but was '
226 + order1.grandtotal__c);
227 System.assert(order1.shippingdiscount__c == 0,
228 'Order shipping discount was not $0 but was '
229 + order1.shippingdiscount__c);
230
231 // Add items to increase subtotal
232 item1 = new Item__C(Price__c = 25, weight__c = 20, quantity__c = 1,
233 Shipping_Invoice__C = order1.id);
234 insert item1;
235
236 // Retrieve the order and verify free shipping is applicable
237 order1 = [SELECT id, subtotal__c, tax__c, shipping__c, totalweight__c,
238 grandtotal__c, shippingdiscount__c
239 FROM Shipping_Invoice__C
240 WHERE Id = :order1.Id];
241
242 // Order total is now at $100, so free shipping should be enabled
243 System.assert(order1.subtotal__c == 100,
244 'Order subtotal was not $100, but was '+ order1.subtotal__c);
245 System.assert(order1.tax__c == 9.25,
246 'Order tax was not $9.25, but was ' + order1.tax__c);
247 System.assert(order1.shipping__c == 19.50,
248 'Order shipping was not $19.50, but was '
249 + order1.shipping__c);
250 System.assert(order1.totalweight__c == 26.00,
251 'Order weight was not 26 but was ' + order1.totalweight__c);
252 System.assert(order1.grandtotal__c == 109.25,
253 'Order grand total was not $86.4375 but was '
254 + order1.grandtotal__c);
255 System.assert(order1.shippingdiscount__c == -19.50,
256 'Order shipping discount was not -$19.50 but was '
257 + order1.shippingdiscount__c);
258 }
259
260 // Negative testing for inserting bad input
261 public static testmethod void testNegativeTests(){
262
263 // Create the shipping invoice. It's a best practice to either use defaults
264 // or to explicitly set all values to zero so as to avoid having
265 // extraneous data in your test.
266 Shipping_Invoice__C order1 = new Shipping_Invoice__C(subtotal__c = 0,
267 totalweight__c = 0, grandtotal__c = 0,
268 ShippingDiscount__c = 0, Shipping__c = 0, tax__c = 0);
269
270 // Insert the order and populate with items.
271 insert Order1;
272 Item__c item1 = new Item__C(Price__c = -10, weight__c = 1, quantity__c = 1,
273 Shipping_Invoice__C = order1.id);
274 Item__c item2 = new Item__C(Price__c = 25, weight__c = -2, quantity__c = 1,
275 Shipping_Invoice__C = order1.id);
276 Item__c item3 = new Item__C(Price__c = 40, weight__c = 3, quantity__c = -1,
277 Shipping_Invoice__C = order1.id);
278 Item__c item4 = new Item__C(Price__c = 40, weight__c = 3, quantity__c = 0,
279 Shipping_Invoice__C = order1.id);
280
281 try{
282 insert item1;
283 }
284 catch(Exception e)
285 {
286 system.assert(e.getMessage().contains('Price must be non-negative'),
287 'Price was negative but was not caught');
288 }
289
290 try{
291 insert item2;
292 }
293 catch(Exception e)
294 {
295 system.assert(e.getMessage().contains('Weight must be non-negative'),
296 'Weight was negative but was not caught');
297 }
298
299 try{
300 insert item3;
301 }
302 catch(Exception e)
303 {
304 system.assert(e.getMessage().contains('Quantity must be positive'),
305 'Quantity was negative but was not caught');
306 }
307
308 try{
309 insert item4;
310 }
311 catch(Exception e)
312 {
313 system.assert(e.getMessage().contains('Quantity must be positive'),
314 'Quantity was zero but was not caught');
315 }
316 }
317}
318