Note: This release is in preview. Features described here don’t become generally available until the latest general availability date that Salesforce announces for this release. Before then, and where features are noted as beta, pilot, or developer preview, we can’t guarantee general availability within any particular time frame or at all. Make your purchase decisions only on the basis of generally available products and features.
Shipping Invoice Example Code
These 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 as system 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 as system 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}