   [storage transaction-id]
   (select-lots-transactions-by-transaction-id storage transaction-id))
 
+; TODO move this to a util namespace
+(defmulti ^:private parse-date-range
+  type)
+
+(defmethod ^:private parse-date-range :default
+  [value]
+  value)
+
+(defmethod ^:private parse-date-range String
+  [value]
+  (let [[year month day] (->> (re-matches #"(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?"
+                                          value)
+                              rest
+                              (map #(when % (Integer. %))))]
+    (cond
+
+      day
+      (t/local-date year month day)
+
+      month
+      [:between
+       (t/first-day-of-the-month year month)
+       (t/last-day-of-the-month year month)]
+
+      :else
+      [:between
+       (t/local-date year 1 1)
+       (t/local-date year 12 31)])))
+
+; TODO Replace these with coercion rules?
+(defn- ensure-id-type
+  [criteria]
+  (if-let [id (:id criteria)]
+    (if (string? id)
+      (assoc criteria :id (java.util.UUID/fromString id))
+      criteria)
+    criteria))
+
+(defn- ensure-transaction-date-type
+  [criteria]
+  (if-let [transaction-date (:transaction-date criteria)]
+    (assoc criteria :transaction-date (parse-date-range transaction-date))
+    criteria))
+
+(defn- prepare-criteria
+  [criteria]
+  (-> criteria
+      ensure-transaction-date-type
+      ensure-id-type))
+
+(defn search-items
+  "Returns transaction items matching the specified criteria"
+  ([storage-spec criteria]
+   (search-items storage-spec criteria {}))
+  ([storage-spec criteria options]
+   (with-storage [s storage-spec]
+     (map after-item-read
+          (select-transaction-items s
+                                    (prepare-criteria criteria)
+                                    options)))))
+
 (defn- append-items
   [{:keys [id transaction-date] :as transaction} storage]
   (when transaction
     (assoc transaction
            :items
-           (mapv after-item-read
-                 (select-transaction-items
-                   storage
-                   {:transaction-id id
-                    :transaction-date transaction-date}
-                   {:sort [[:action :desc] [:quantity :desc]]})))))
+           (vec (search-items storage {:transaction-id id
+                                       :transaction-date transaction-date}
+                              {:sort [[:action :desc] [:quantity :desc]]})))))
 
 (defn- append-lot-items
   [transaction storage]

(s/def ::per-page validation/positive-integer?)
(s/def ::select-options (s/keys :req-un [::page ::per-page]))

(defmulti ^:private parse-date-range
  type)

(defmethod ^:private parse-date-range :default
  [value]
  value)

(defmethod ^:private parse-date-range String
  [value]
  (let [[year month day] (->> (re-matches #"(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?"
                                          value)
                              rest
                              (map #(when % (Integer. %))))]
    (cond

      day
      (t/local-date year month day)

      month
      [:between
       (t/first-day-of-the-month year month)
       (t/last-day-of-the-month year month)]

      :else
      [:between
       (t/local-date year 1 1)
       (t/local-date year 12 31)])))

(defn- ensure-transaction-date-type
  [criteria]
  (if-let [transaction-date (:transaction-date criteria)]
    (assoc criteria :transaction-date (parse-date-range transaction-date))
    criteria))

(defn- ensure-id-type
  [criteria]
  (if-let [id (:id criteria)]
    (if (string? id)
      (assoc criteria :id (java.util.UUID/fromString id))
      criteria)
    criteria))

(defn- prepare-criteria
  [criteria]
  (-> criteria
      ensure-transaction-date-type
      ensure-id-type))

(defn search
  "Returns the transactions that belong to the specified entity"
  ([storage-spec criteria]

  ([storage-spec account-id date-spec]
   (items-by-account storage-spec account-id date-spec {}))
  ([storage-spec account-id date-spec options]
   (search-items storage-spec
                 {:account-id account-id
                  :transaction-date (if (coll? date-spec)
                                      [:between
                                       (first date-spec)
                                       (second date-spec)]
                                      date-spec)}
                 (merge options
                        {:sort [[:transaction-date :desc]
                                [:index :desc]]}))))

(defn unreconciled-items-by-account
  "Returns the unreconciled transaction items for the specified account"
  [storage-spec account-id]
  (search storage-spec {:account-id account-id
                        :reconciliation-id nil}))

(defn reload
  "Returns an updated copy of the transaction"
