diff options
| author | Tim <contact@bytim.eu> | 2026-02-22 15:25:50 +0100 |
|---|---|---|
| committer | Tim <contact@bytim.eu> | 2026-02-22 15:25:50 +0100 |
| commit | f279e20468fb5323c33cbf43346c35ddef7f96e0 (patch) | |
| tree | c488ee2791296917367f704524fa8e41a0b518ea /src/cashflow/database.clj | |
Initial commit
Diffstat (limited to 'src/cashflow/database.clj')
| -rw-r--r-- | src/cashflow/database.clj | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/src/cashflow/database.clj b/src/cashflow/database.clj new file mode 100644 index 0000000..6daae1f --- /dev/null +++ b/src/cashflow/database.clj @@ -0,0 +1,87 @@ +(ns cashflow.database + (:require [datalevin.core :as dcore] + [cashflow.frontend.utils :as cfutils] + [cashflow.utils :as cutils] + [clojure.string :as cstr]) + (:import java.time.LocalDate)) + +(def ^:private schema {:type {:db/valueType :db.type/keyword} + :user {:db/valueType :db.type/string} ; TODO: multi user + :description {:db/valueType :db.type/string} + :amount {:db/valueType :db.type/float} + :date {:db/valueType :db.type/string} + :from {:db/valueType :db.type/string} + :to {:db/valueType :db.type/string} + :month-interval {:db/valueType :db.type/bigint}}) + +(def connection (delay (dcore/get-conn "./cashflow.db" schema))) + +(defn create-transaction! + "Stores one transaction `x` in the database." + [x] + (dcore/transact! @connection [x])) + +(defn list-transactions [type] + (->> (dcore/q '[:find (pull ?e [*]) + :in $ ?type + :where [?e :type ?type]] + (dcore/db @connection) type) + (map first))) + +(defn list-one-time-transactions [year month] + (->> (list-transactions :one-time) + (filter (fn [transaction] + (let [transaction-date (LocalDate/parse (:date transaction))] + (and (= year (.getYear transaction-date)) + (= month (.getMonthValue transaction-date)))))) + (sort-by :date))) + +(defn list-recurring-transactions [] + (let [today (LocalDate/now)] + (->> (list-transactions :recurring) + (sort-by (fn [transaction] + (let [from (try (LocalDate/parse (:from transaction)) (catch Exception _ nil)) + to (try (LocalDate/parse (:from transaction)) (catch Exception _ nil))] + (cond + (or (and (nil? from) (nil? to)) + (and (nil? from) (some? to) (.isBefore today to)) + (and (some? to) (.isAfter today from))) 0 + (try (.isBefore today from) + (catch Exception _ false)) 1 + (try (.isAfter today to) + (catch Exception _ false)) 2))))))) + +(defn recurring-transactions-applying-total + ([date] + (->> (list-transactions :recurring) + (filter (fn [transaction] + (let [from (try (LocalDate/parse (:from transaction)) (catch Exception _ nil)) + to (try (LocalDate/parse (:to transaction)) (catch Exception _ nil))] + (try (or (and (nil? from) (nil? to)) + (and (nil? from) (or (.isAfter to date) (.isEqual to date))) + (and (nil? to) (or (.isBefore from date) (.isEqual from date))) + (and (or (.isAfter to date) (.isEqual to date)) + (or (.isBefore from date) (.isEqual from date)))) + (catch Exception _ false))))) + (map (fn [transaction] (/ (:amount transaction) (:month-interval transaction)))) + (apply +))) + ([year month] + (recurring-transactions-applying-total (LocalDate/parse (str (cutils/string-min-length (str year) 4 "0" :before) "-" + (cutils/string-min-length (str month) 2 "0" :before) "-01"))))) + +(defn one-time-transactions-total [year month] + (->> (list-one-time-transactions year month) + (map #(-> % first :amount)) + (apply +) + (+ (recurring-transactions-applying-total year month)))) + +(defn id->transaction [id] + (-> (dcore/q '[:find (pull ?id [*]) + :in $ ?id + :where [?id]] + (dcore/db @connection) id) + first first)) + +(defn delete-transaction! [id] + (dcore/transact! @connection [[:db/retractEntity id]])) + |
