From 6ab9d89dfd3ac423e9e048e12abfbe411b624939 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Thu, 11 Apr 2019 22:46:30 -0700 Subject: [PATCH] adding most simple balance sheet report. --- src/clj/auto_ap/datomic.clj | 23 ++++ src/clj/auto_ap/datomic/ledger.clj | 40 ++----- src/clj/auto_ap/datomic/transactions.clj | 27 +---- src/clj/auto_ap/graphql.clj | 19 +++ src/clj/auto_ap/graphql/ledger.clj | 77 +++++++++++- src/clj/auto_ap/ledger.clj | 3 +- src/cljs/auto_ap/routes.cljs | 4 +- src/cljs/auto_ap/subs.cljs | 1 - src/cljs/auto_ap/views/main.cljs | 8 ++ src/cljs/auto_ap/views/pages/ledger.cljs | 14 +-- .../views/pages/ledger/balance_sheet.cljs | 113 ++++++++++++++++++ .../views/pages/ledger/profit_and_loss.cljs | 11 ++ .../auto_ap/views/pages/ledger/side_bar.cljs | 32 +++++ 13 files changed, 298 insertions(+), 74 deletions(-) create mode 100644 src/cljs/auto_ap/views/pages/ledger/balance_sheet.cljs create mode 100644 src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs create mode 100644 src/cljs/auto_ap/views/pages/ledger/side_bar.cljs diff --git a/src/clj/auto_ap/datomic.clj b/src/clj/auto_ap/datomic.clj index 2ef5ce78..6e4e511e 100644 --- a/src/clj/auto_ap/datomic.clj +++ b/src/clj/auto_ap/datomic.clj @@ -761,5 +761,28 @@ +(defn add-sorter-field [q sort-map args] + (merge-query q + {:query {:find ['?sorter] + :where (sort-map + (:sort-by args) + (println "Warning, trying to sort by unsupported field" (:sort-by args)))}})) +(defn apply-sort [args sort-fn results ] + (cond->> results + sort-fn (sort-by sort-fn) + (= (:asc args) false) (reverse))) +(defn apply-sort-2 [args results ] + (let [comparator (if (= (:asc args) false) + (fn [x y] (compare y x)) + (fn [x y] (compare x y)))] + (sort comparator results ))) + +(defn apply-pagination [args results] + + {:ids (->> results + (drop (:start args 0)) + (take (:count args 100)) + (map last)) + :count (count results)}) diff --git a/src/clj/auto_ap/datomic/ledger.clj b/src/clj/auto_ap/datomic/ledger.clj index b124fb78..45bf48cd 100644 --- a/src/clj/auto_ap/datomic/ledger.clj +++ b/src/clj/auto_ap/datomic/ledger.clj @@ -1,7 +1,7 @@ (ns auto-ap.datomic.ledger (:require [datomic.api :as d] [auto-ap.graphql.utils :refer [->graphql limited-clients]] - [auto-ap.datomic :refer [merge-query ]] + [auto-ap.datomic :refer [merge-query apply-sort-2 apply-sort apply-pagination add-sorter-field]] [auto-ap.datomic :refer [uri]] [clj-time.coerce :as c])) @@ -16,33 +16,8 @@ :else (keyword "transaction" sort-by))) -(defn add-sorter-field [q sort-map args] - (merge-query q - {:query {:find ['?sorter] - :where (sort-map - (:sort-by args) - (println "Warning, trying to sort by unsupported field" (:sort-by args)))}})) - -(defn apply-sort [args sort-fn results ] - (cond->> results - sort-fn (sort-by sort-fn) - (= (:asc args) false) (reverse))) - -(defn apply-sort-2 [args results ] - (let [comparator (if (= (:asc args) false) - (fn [x y] (compare y x)) - (fn [x y] (compare x y)))] - (sort comparator results ))) - -(defn apply-pagination [args results] - - {:ids (->> results - (drop (:start args 0)) - (take (:count args 100)) - (map last)) - :count (count results)}) - (defn raw-graphql-ids [db args] + (println args) (let [query (cond-> {:query {:find [] :in ['$ ] :where []} @@ -59,16 +34,17 @@ :where ['[?e :journal-entry/client ?xx]]} :args [(set (map :db/id (limited-clients (:id args))))]}) - #_(:bank-account-id args) - #_(merge-query {:query {:in ['?bank-account-id] - :where ['[?e :transaction/bank-account ?bank-account-id]]} - :args [(:bank-account-id args)]}) - (:client-id args) (merge-query {:query {:in ['?client-id] :where ['[?e :journal-entry/client ?client-id]]} :args [(:client-id args)]}) + (:date-before args) + (merge-query {:query {:in ['?date-before] + :where ['[?e :journal-entry/date ?d] + '[(<= ?d ?date-before)]]} + :args [(:date-before args)]}) + true (merge-query {:query {:find ['?e] :where ['[?e :journal-entry/original-entity]]}}))] (cond->> query diff --git a/src/clj/auto_ap/datomic/transactions.clj b/src/clj/auto_ap/datomic/transactions.clj index fa164f12..34f0a3dc 100644 --- a/src/clj/auto_ap/datomic/transactions.clj +++ b/src/clj/auto_ap/datomic/transactions.clj @@ -1,6 +1,6 @@ (ns auto-ap.datomic.transactions (:require [datomic.api :as d] - [auto-ap.datomic :refer [uri merge-query]] + [auto-ap.datomic :refer [uri merge-query apply-sort-2 apply-sort apply-pagination add-sorter-field]] [auto-ap.graphql.utils :refer [limited-clients]] [clj-time.coerce :as c])) @@ -15,31 +15,6 @@ :else (keyword "transaction" sort-by))) -(defn add-sorter-field [q sort-map args] - (merge-query q - {:query {:find ['?sorter] - :where (sort-map - (:sort-by args) - (println "Warning, trying to sort by unsupported field" (:sort-by args)))}})) - -(defn apply-sort [args sort-fn results ] - (cond->> results - sort-fn (sort-by sort-fn) - (= (:asc args) false) (reverse))) - -(defn apply-sort-2 [args results ] - (let [comparator (if (= (:asc args) false) - (fn [x y] (compare y x)) - (fn [x y] (compare x y)))] - (sort comparator results ))) - -(defn apply-pagination [args results] - - {:ids (->> results - (drop (:start args 0)) - (take (:count args 100)) - (map last)) - :count (count results)}) (defn raw-graphql-ids [db args] (let [query (cond-> {:query {:find [] diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 6844614e..7ab8c784 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -67,6 +67,19 @@ :bank_code {:type 'String} :bank_name {:type 'String} :yodlee_account_id {:type 'Int}}} + :balance_sheet_account + {:fields { + :amount {:type 'String} + :name {:type 'String}}} + + :balance_sheet_grouping + {:fields {:accounts {:type '(list :balance_sheet_account)} + :name {:type 'String} + :grouping_type {:type 'String}}} + + :balance_sheet + {:fields {:balance_sheet_groupings {:type '(list :balance_sheet_grouping)}}} + :address {:fields {:street1 {:type 'String} :street2 {:type 'String} @@ -256,6 +269,11 @@ :args {:client_id {:type :id}} :resolve :get-invoice-stats} + :balance_sheet {:type :balance_sheet + :args {:client_id {:type :id} + :date {:type 'String}} + :resolve :get-balance-sheet} + :invoice_page {:type '(list :invoice_page) :args {:import_status {:type 'String} :status {:type 'String} @@ -612,6 +630,7 @@ :get-accounts gq-accounts/get-accounts :get-transaction-page gq-transactions/get-transaction-page :get-ledger-page gq-ledger/get-ledger-page + :get-balance-sheet gq-ledger/get-balance-sheet :get-expense-account-stats get-expense-account-stats :get-invoice-stats get-invoice-stats diff --git a/src/clj/auto_ap/graphql/ledger.clj b/src/clj/auto_ap/graphql/ledger.clj index 3d93660c..660fd64a 100644 --- a/src/clj/auto_ap/graphql/ledger.clj +++ b/src/clj/auto_ap/graphql/ledger.clj @@ -1,8 +1,10 @@ (ns auto-ap.graphql.ledger - (:require [datomic.api :as d] + (:require [auto-ap.datomic :refer [uri]] [auto-ap.datomic.ledger :as l] - [auto-ap.graphql.utils :refer [->graphql <-graphql]] - [auto-ap.datomic :refer [uri merge-query]])) + [auto-ap.graphql.utils :refer [->graphql <-graphql limited-clients]] + [clj-time.coerce :as coerce] + [clojure.string :as str] + [datomic.api :as d])) (defn get-ledger-page [context args value] (let [args (assoc args :id (:id context)) @@ -13,5 +15,72 @@ :count (count journal-entries) :start (:start args 0) :end (+ (:start args 0) (count journal-entries))})) +(defn credit-account? [account] + (= (:account/numeric-code account ) 2110)) -#_(get-ledger-page nil nil nil) +(defn debit-account? [account] + (not (credit-account? account))) + +(defn expense-account? [account] + (and (:account/name account) + (not (str/starts-with? (:account/name account "") "A")))) + + +(defn get-balance-sheet [context args value] + (let [args (assoc args :id (:id context)) + [results] (l/get-graphql {:client-id (:client_id args) + :date-before (coerce/to-date (:date args)) + :count Integer/MAX_VALUE}) + _ (println results) + + accounts (->> results + (mapcat :journal-entry/line-items) + (group-by :journal-entry-line/account) + (reduce-kv + (fn [result account line-items] + (if (expense-account? account) + result + (update-in result [(if (credit-account? account) + "Liabilities" + "Assets") + (if (credit-account? account) + "Accounts Payable" + "1100 Cash and Bank Accounts" ) + ] + conj + {:name (or (:bank-account/name account) (:account/name account)) + :amount (reduce + 0 (map + (fn [line-item] + (cond + (and (credit-account? account) (:journal-entry-line/debit line-item)) + (- (:journal-entry-line/debit line-item)) + + (and (credit-account? account) (:journal-entry-line/credit line-item)) + (:journal-entry-line/credit line-item) + + (and (debit-account? account) (:journal-entry-line/debit line-item)) + (:journal-entry-line/debit line-item) + + (and (debit-account? account) (:journal-entry-line/credit line-item)) + (- (:journal-entry-line/credit line-item)))) + line-items))})) + + ) {}))] + (->graphql + {:balance-sheet-groupings + (-> [] + (into (->> (get accounts "Assets") + (map (fn [[n accounts]] + {:name n + :grouping-type "Assets" + :accounts accounts} + )))) + (into (->> (get accounts "Liabilities") + (map (fn [[n accounts]] + {:name n + :grouping-type "Liabilities" + :accounts accounts} + )))))}))) + +#_(get-balance-sheet nil {:client_id 17592186046468 + :date "2017-03-01"} nil) diff --git a/src/clj/auto_ap/ledger.clj b/src/clj/auto_ap/ledger.clj index c118755a..85e106de 100644 --- a/src/clj/auto_ap/ledger.clj +++ b/src/clj/auto_ap/ledger.clj @@ -63,7 +63,7 @@ (remove-nils {:journal-entry/source "transaction" :journal-entry/client (:db/id (:transaction/client entity)) - :journal-entry/date (:transaction/date entity) + :journal-entry/date (doto (:transaction/date entity) println) :journal-entry/original-entity (:db/id entity) :journal-entry/vendor (:db/id (:transaction/vendor entity)) :journal-entry/amount (Math/abs (:transaction/amount entity)) @@ -130,7 +130,6 @@ (process-one (d/tx-report-queue (d/connect uri) )))) - #_(process-one (d/tx-report-queue (d/connect uri) )) #_(process-all) diff --git a/src/cljs/auto_ap/routes.cljs b/src/cljs/auto_ap/routes.cljs index aa9cc94d..3e8b9886 100644 --- a/src/cljs/auto_ap/routes.cljs +++ b/src/cljs/auto_ap/routes.cljs @@ -21,4 +21,6 @@ "voided" :voided-invoices "new" :new-invoice} "transactions/" {"" :transactions} - "ledger/" {"" :ledger}}]) + "ledger/" {"" :ledger + "profit-and-loss" :profit-and-loss + "balance-sheet" :balance-sheet}}]) diff --git a/src/cljs/auto_ap/subs.cljs b/src/cljs/auto_ap/subs.cljs index 50afcc7c..9277b221 100644 --- a/src/cljs/auto_ap/subs.cljs +++ b/src/cljs/auto_ap/subs.cljs @@ -30,7 +30,6 @@ :<- [::clients] :<- [::client] (fn [[clients client]] - (println client) (if client (:bank-accounts client) (reduce (fn [result {:keys [bank-accounts]}] diff --git a/src/cljs/auto_ap/views/main.cljs b/src/cljs/auto_ap/views/main.cljs index 98b953e2..9367543a 100644 --- a/src/cljs/auto_ap/views/main.cljs +++ b/src/cljs/auto_ap/views/main.cljs @@ -13,6 +13,8 @@ [auto-ap.views.pages.needs-activation :refer [needs-activation-page]] [auto-ap.views.pages.transactions :refer [transactions-page]] [auto-ap.views.pages.ledger :refer [ledger-page]] + [auto-ap.views.pages.ledger.balance-sheet :refer [balance-sheet-page]] + [auto-ap.views.pages.ledger.profit-and-loss :refer [profit-and-loss-page]] [auto-ap.views.pages.login :refer [login-page]] [auto-ap.views.pages.checks :refer [checks-page]] [auto-ap.views.pages.admin :refer [admin-page]] @@ -50,6 +52,12 @@ (defmethod page :ledger [_] (ledger-page)) +(defmethod page :profit-and-loss [_] + (profit-and-loss-page)) + +(defmethod page :balance-sheet [_] + (balance-sheet-page)) + (defmethod page :admin [_] (admin-page)) diff --git a/src/cljs/auto_ap/views/pages/ledger.cljs b/src/cljs/auto_ap/views/pages/ledger.cljs index 90def380..f0ff967c 100644 --- a/src/cljs/auto_ap/views/pages/ledger.cljs +++ b/src/cljs/auto_ap/views/pages/ledger.cljs @@ -4,14 +4,18 @@ [auto-ap.subs :as subs] [auto-ap.views.components.bank-account-filter :refer [bank-account-filter]] [auto-ap.views.components.layouts :refer [appearing-side-bar side-bar-layout]] + [auto-ap.routes :as routes] [auto-ap.views.components.modal :refer [action-modal]] + [bidi.bidi :as bidi] [auto-ap.views.components.paginator :refer [paginator]] [auto-ap.views.components.sorter :refer [sorted-column]] [auto-ap.views.pages.ledger.table :as table] + [auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]] [auto-ap.views.pages.transactions.common :refer [transaction-read]] + [auto-ap.utils :refer [replace-by]] [auto-ap.views.pages.transactions.manual :as manual] - [auto-ap.views.utils :refer [bind-field date->str dispatch-event nf]] + [auto-ap.views.utils :refer [bind-field date->str dispatch-event nf active-when]] [goog.string :as gstring] [re-frame.core :as re-frame])) @@ -90,12 +94,6 @@ (defn ledger-page [] [side-bar-layout - {:side-bar [:div - [:p.menu-label "Bank Account"] - [:div - [bank-account-filter - {:on-change-event [::change-selected-bank-account] - :value (:bank-acount-filter @(re-frame/subscribe [::ledger-page])) - :bank-accounts @(re-frame/subscribe [::subs/bank-accounts])}]]] + {:side-bar [ledger-side-bar] :main [ledger-content]}]) diff --git a/src/cljs/auto_ap/views/pages/ledger/balance_sheet.cljs b/src/cljs/auto_ap/views/pages/ledger/balance_sheet.cljs new file mode 100644 index 00000000..e0478bcf --- /dev/null +++ b/src/cljs/auto_ap/views/pages/ledger/balance_sheet.cljs @@ -0,0 +1,113 @@ +(ns auto-ap.views.pages.ledger.balance-sheet + (:require [auto-ap.subs :as subs] + [auto-ap.views.components.layouts :refer [side-bar-layout]] + [goog.string :as gstring] + [auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]] + [auto-ap.views.utils :refer [date->str date-picker bind-field]] + [cljs-time.core :as t] + [re-frame.core :as re-frame])) + +(re-frame/reg-sub + ::report + (fn [db] + (-> db ::report))) + +(re-frame/reg-sub + ::assets + (fn [db] + (->> db ::report :balance-sheet-groupings (filter (fn [{:keys [grouping-type]}] (= "Assets" grouping-type)))))) + +(re-frame/reg-sub + ::liabilities + (fn [db] + (->> db ::report :balance-sheet-groupings (filter (fn [{:keys [grouping-type]}] (= "Liabilities" grouping-type)))))) + +(re-frame/reg-event-db + ::received + (fn [db [_ data]] + (-> db + (assoc ::report (:balance-sheet data)) + (assoc-in [:status :loading] false)))) + +(re-frame/reg-sub + ::params + (fn [db] + (-> db ::params))) + +(re-frame/reg-event-fx + ::params-change + (fn [cofx [_ params]] + {:db (-> (:db cofx) + (assoc-in [:status :loading] true) + (assoc-in [::params] params)) + :graphql {:token (-> cofx :db :user) + :query-obj {:venia/queries [[:balance-sheet + (assoc params + :client-id (:id @(re-frame/subscribe [::subs/client]))) + [[:balance-sheet-groupings [:grouping-type :name [:accounts [:name :amount]]]]]]]} + :on-success [::received]}})) + +(re-frame/reg-event-fx + ::date-picked + (fn [cofx [_ _ date]] + (println "DATE" date) + + {:dispatch [::params-change (assoc @(re-frame/subscribe [::params]) + :date + date)]})) + +(defn grouping [groupings] + + [:table.table + (for [grouping groupings] + (list + [:tr [:td {:col-span "2"} "----" (:name grouping) "----"]] + (for [account (:accounts grouping)] + + [:tr + [:td (:name account) ] + [:td.has-text-right (gstring/format "$%.2f" (:amount account))]] + + ) + + [:tr [:td "----" (:name grouping) "----"] + [:td.has-text-right + (->> grouping + :accounts + (map :amount) + (map #(js/parseFloat %)) + (reduce + 0) + (gstring/format "$%.2f" ))]]))]) + +(def balance-sheet-content + (with-meta + (fn [] + (let [current-client @(re-frame/subscribe [::subs/client]) + user @(re-frame/subscribe [::subs/user]) + params @(re-frame/subscribe [::params])] + [:div + [:h1.title "Balance Sheet"] + [bind-field + [date-picker {:class-name "input" + :class "input" + :format-week-number (fn [] "") + :previous-month-button-label "" + :placeholder "mm/dd/yyyy" + :next-month-button-label "" + :next-month-label "" + :type "date" + :field [:date] + :event [::date-picked] + :popper-props (clj->js {:placement "right"}) + :subscription params}]] + [:h2.title "Assets"] + [grouping @(re-frame/subscribe [::assets])] + [:h2.title "Liabilities"] + [grouping @(re-frame/subscribe [::liabilities])] + ])) + {:component-will-mount #(re-frame/dispatch-sync [::params-change {}]) })) + +(defn balance-sheet-page [] + [side-bar-layout + {:side-bar [ledger-side-bar] + :main [balance-sheet-content]}]) diff --git a/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs b/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs new file mode 100644 index 00000000..cb02056e --- /dev/null +++ b/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs @@ -0,0 +1,11 @@ +(ns auto-ap.views.pages.ledger.profit-and-loss + (:require [auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]] + [auto-ap.views.components.layouts :refer [side-bar-layout]])) + +(defn profit-and-loss-content [] + [:div "Profit and loss"]) + +(defn profit-and-loss-page [] + [side-bar-layout + {:side-bar [ledger-side-bar] + :main [profit-and-loss-content]}]) diff --git a/src/cljs/auto_ap/views/pages/ledger/side_bar.cljs b/src/cljs/auto_ap/views/pages/ledger/side_bar.cljs new file mode 100644 index 00000000..1ff10949 --- /dev/null +++ b/src/cljs/auto_ap/views/pages/ledger/side_bar.cljs @@ -0,0 +1,32 @@ +(ns auto-ap.views.pages.ledger.side-bar + (:require [auto-ap.routes :as routes] + [auto-ap.subs :as subs] + [auto-ap.views.utils :refer [active-when]] + [bidi.bidi :as bidi] + [re-frame.core :as re-frame])) + +(defn ledger-side-bar [] + (let [ap @(re-frame/subscribe [::subs/active-page])] + [:div + + [:ul.menu-list + [:li.menu-item + [:a.item {:href (bidi/path-for routes/routes :ledger) + :class [(active-when ap = :ledger)]} + [:span {:class "icon icon-accounting-invoice-mail" :style {:font-size "25px"}}] + [:span {:class "name"} "Register"]]] + [:li.menu-item + [:a.item {:href (bidi/path-for routes/routes :profit-and-loss) + :class [(active-when ap = :profit-and-loss)]} + + [:span {:class "icon icon-check-payment-give" :style {:font-size "25px"}}] + + [:span {:class "name"} "Profit & Loss"]]] + [:li.menu-item + [:a.item {:href (bidi/path-for routes/routes :balance-sheet) + :class [(active-when ap = :balance-sheet)]} + + [:span {:class "icon icon-bin-2" :style {:font-size "25px"}}] + + [:span {:class "name"} "Balance Sheet"]]] + ]]))