Full example

// source: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_shopping_cart_example_code.htm

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

System.assert(i.quantity__c > 0, 'Quantity must be positive');
System.assert(i.weight__c >= 0, 'Weight must be non-negative');
System.assert(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];

// 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 updateMap.values();
}