diff -rup storm.dev/storminvoice/storminvoice.module storm/storminvoice/storminvoice.module
--- storm.dev/storminvoice/storminvoice.module	2009-05-28 10:19:46.000000000 +1000
+++ storm/storminvoice/storminvoice.module	2009-06-01 14:53:33.000000000 +1000
@@ -447,6 +447,8 @@ function storminvoice_insert($node) {
           $node->amount, $node->tax, $node->total, $node->totalcustomercurr,
           $node->requestdate, $node->duedate, $node->paymentdate, $node->taxexempt);
 
+  _storminvoice_aftersave($node);
+
   $_REQUEST['destination'] = 'node/'. $node->nid .'/invoiceitems';
 }
 
@@ -491,6 +493,86 @@ function _storminvoice_beforesave(&$node
   }
 }
 
+// For invoice items we need to discover the hourly rate
+// Perhaps this should be elsewhere?
+
+function _storminvoice_hourly_price($project_nid,  $task_nid, $ticket_nid) {
+  // TODO: There should be some caching here
+
+  // Try and get ticket, then task, then project
+  $row = db_fetch_object(db_query('SELECT pricemode, price, currency FROM {stormticket} WHERE nid = %d', $ticket_nid));
+
+  if (!$row) {
+    $row = db_fetch_object(db_query('SELECT pricemode, price, currency FROM {stormtask} WHERE nid = %d', $task_nid));
+  }
+
+  if (!$row) {
+    $row = db_fetch_object(db_query('SELECT pricemode, price, currency FROM {stormproject} WHERE nid = %d', $project_nid));
+  }
+
+  // If no price found it's zero
+  if (!$row) { return 0; }
+
+  // If it's fixed then price is zero
+  if ($row->pricemode == t('fixed')) { return 0; }
+
+  //TODO: More options for daily?
+  if ($row->pricemode == t('daily')) { return $row->price * 8; }
+
+  //TODO: error check for ! hourly
+  // Just return the rate as is assumed hourly.
+  return $row->price;
+}
+
+// Maybe it would be nice to call this standalone with a link
+// from the invoice display that is called something like:
+// 'Add all unbilled items now'
+
+function _storminvoice_auto_items(&$node) {
+  // Go find all the non-billed timetrackings
+  // TODO: add expenses, etc.
+
+  // If invoice is for specific project limit to that.
+
+  $add_where = "AND stt.organization_nid = $node->organization_nid";
+  if ($node->project_nid) {
+    $add_where .= " AND stt.project_nid = $node->project_nid";
+  }
+
+  // Should this include src_vid? Hmmmm...
+  $sql = "SELECT n.title AS title,
+                 stt.nid,
+                 stt.vid,
+                 stt.duration AS duration,
+                 stt.organization_nid,
+                 stt.project_nid,
+                 stt.task_nid,
+                 stt.ticket_nid
+          FROM {stormtimetracking} stt
+          JOIN {node} n ON (n.nid = stt.nid AND n.vid = stt.vid)
+          WHERE stt.billable = 1 $add_where
+          AND NOT EXISTS (SELECT 1 FROM {storminvoiceitem} WHERE src_nid = stt.nid)";
+  $result = db_query($sql);
+  while ($row = db_fetch_object($result)) {
+    // Figure out what the hourly rate is for this org/project/task/ticket
+    $price = _storminvoice_hourly_price($row->project_nid, $row->task_nid, $row->ticket_nid);
+    $item_node = new StdClass();
+    $item_node->type = 'storminvoiceitem';
+    $item_node->status = 1; // published
+    $item_node->title = $row->title;
+    $item_node->invoice_nid = $node->nid;
+    $item_node->src_nid = $row->nid;
+    $item_node->src_vid = $row->vid;
+    $item_node->amount = $row->duration * $price;
+
+    node_save($item_node);
+  }
+}
+
+function _storminvoice_aftersave(&$node) {
+  _storminvoice_auto_items($node);
+}
+
 function storminvoice_nodeapi(&$node, $op, $teaser, $page) {
   switch ($op) {
     case 'delete revision':
diff -rup storm.dev/storminvoiceitem/storminvoiceitem.install storm/storminvoiceitem/storminvoiceitem.install
--- storm.dev/storminvoiceitem/storminvoiceitem.install	2009-02-15 20:03:33.000000000 +1100
+++ storm/storminvoiceitem/storminvoiceitem.install	2009-06-01 14:51:27.000000000 +1000
@@ -39,3 +39,9 @@ function storminvoiceitem_update_1() {
   return $ret;
 }
 
+function storminvoiceitem_update_2() {
+  $ret = array();
+  db_add_column($ret, 'storminvoiceitem', 'src_nid', 'int');
+  db_add_column($ret, 'storminvoiceitem', 'src_vid', 'int');
+  return $ret;
+}
diff -rup storm.dev/storminvoiceitem/storminvoiceitem.module storm/storminvoiceitem/storminvoiceitem.module
--- storm.dev/storminvoiceitem/storminvoiceitem.module	2009-05-29 08:20:25.000000000 +1000
+++ storm/storminvoiceitem/storminvoiceitem.module	2009-06-01 14:52:21.000000000 +1000
@@ -265,15 +265,19 @@ function storminvoiceitem_insert($node) 
            (vid, nid,
            invoice_nid,
            amount, taxpercent, tax, total,
-           weight) VALUES
+           weight,
+           src_nid, src_vid) VALUES
            (%d, %d,
            %d,
            %f, %f, %f, %f,
-           %d)",
+           %d,
+           %d, %d)",
           $node->vid, $node->nid,
           $node->invoice_nid,
           $node->amount, $node->taxpercent, $node->tax, $node->total,
-          $node->weight);
+          $node->weight,
+          $node->src_nid,
+          $node->src_vid);
   _storminvoiceitem_aftersave($node);
 }
 
