From e79c0e1575cd3f047165a361f392c8bdf6bf840d Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Sat, 13 Apr 2019 13:36:21 -0700 Subject: [PATCH] basic support for pnl --- .../datomic/migrate/add_general_ledger.clj | 3 +- src/clj/auto_ap/graphql.clj | 7 + src/clj/auto_ap/graphql/ledger.clj | 95 +++++++++++- .../views/pages/ledger/profit_and_loss.cljs | 139 +++++++++++++++++- 4 files changed, 237 insertions(+), 7 deletions(-) diff --git a/src/clj/auto_ap/datomic/migrate/add_general_ledger.clj b/src/clj/auto_ap/datomic/migrate/add_general_ledger.clj index 0c5f9fbd..71c40ad4 100644 --- a/src/clj/auto_ap/datomic/migrate/add_general_ledger.clj +++ b/src/clj/auto_ap/datomic/migrate/add_general_ledger.clj @@ -166,10 +166,11 @@ z (->> transaction-ids (map #(ledger/entity-change->ledger (d/db conn) [:transaction %])) (filter identity) - (mapv identity))] + (mapv #(vector %)))] z)) #_(test-run (bulk-load-transaction-ledger (d/connect auto-ap.datomic/uri))) +#_(test-run (bulk-load-invoice-ledger (d/connect auto-ap.datomic/uri))) #_(do (doseq [tran (convert-transactions (d/connect auto-ap.datomic/uri))] @(d/transact (d/connect auto-ap.datomic/uri) tran)) diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 4ea56b0c..cfe59bde 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -265,6 +265,12 @@ :date {:type 'String}} :resolve :get-balance-sheet} + :profit_and_loss {:type :balance_sheet + :args {:client_id {:type :id} + :from_date {:type 'String} + :to_date {:type 'String}} + :resolve :get-profit-and-loss} + :invoice_page {:type '(list :invoice_page) :args {:import_status {:type 'String} :status {:type 'String} @@ -638,6 +644,7 @@ :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-profit-and-loss gq-ledger/get-profit-and-loss :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 59662e64..8d50d26c 100644 --- a/src/clj/auto_ap/graphql/ledger.clj +++ b/src/clj/auto_ap/graphql/ledger.clj @@ -86,5 +86,96 @@ :accounts accounts} )))))}))) -#_(get-balance-sheet nil {:client_id 17592186046468 - :date "2017-03-01"} nil) + +(defn get-profit-and-loss [context args value] + (let [args (assoc args :id (:id context)) + [starting-results] (l/get-graphql {:client-id (:client_id args) + :date-before (coerce/to-date (:from_date args)) + :count Integer/MAX_VALUE}) + [ending-results] (l/get-graphql {:client-id (:client_id args) + :date-before (coerce/to-date (:to_date args)) + :count Integer/MAX_VALUE}) + _ (println (:from_date args) (:to_date args)) + + roll-up (fn [results] + (->> results + (mapcat :journal-entry/line-items) + (group-by :journal-entry-line/account) + (reduce-kv (fn [result account line-items] + (assoc result + account + {: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)) + + :else + 0)) + line-items))})) + {}) + )) + + starting-accounts (roll-up starting-results) + ending-accounts (roll-up ending-results) + accounts (into + starting-accounts + ending-accounts) + + accounts (reduce-kv + (fn [result account line-item] + (update-in result [(cond + (expense-account? account) + "Expenses" + + (credit-account? account) + "Liabilities" + (debit-account? account) + "Assets") + + (if (credit-account? account) + "Accounts Payable" + "1100 Cash and Bank Accounts" ) + ] + conj + (update line-item :amount #(- % (:amount (starting-accounts account 0) 0)) ) + )) + {} + ending-accounts) + ] + (->graphql + {:balance-sheet-groupings + (-> [] + (into (->> (get accounts "Assets") + (map (fn [[n accounts]] + {:name n + :grouping-type "Assets" + :accounts accounts} + )))) + + (into (->> (get accounts "Expenses") + (map (fn [[n accounts]] + {:name n + :grouping-type "Expenses" + :accounts accounts} + )))) + (into (->> (get accounts "Liabilities") + (map (fn [[n accounts]] + {:name n + :grouping-type "Liabilities" + :accounts accounts} + )))))}))) + +#_(get-profit-and-loss nil {:client_id [:client/code "CBC"] + :from_date "2018-01-01" + :to_date "2019-04-01"} nil) 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 index cb02056e..669409d9 100644 --- a/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs @@ -1,9 +1,140 @@ (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]])) + (:require [auto-ap.subs :as subs] + [auto-ap.views.components.layouts :refer [side-bar-layout]] + [goog.string :as gstring] + [auto-ap.utils :refer [dollars-0?]] + [auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]] + [auto-ap.views.utils :refer [date->str date-picker bind-field standard]] + [cljs-time.core :as t] + [re-frame.core :as re-frame])) -(defn profit-and-loss-content [] - [:div "Profit and loss"]) + +(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 + ::expenses + (fn [db] + (->> db ::report :balance-sheet-groupings (filter (fn [{:keys [grouping-type]}] (= "Expenses" 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]] + (println (:profit-and-loss data)) + (-> db + (assoc ::report (:profit-and-loss 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 [[:profit-and-loss + (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 [_ f date]] + (println date) + {:dispatch [::params-change (assoc-in @(re-frame/subscribe [::params]) + f + date)]})) + +(defn grouping [groupings] + + [:table.table + (for [grouping groupings] + (list + [:tr [:td {:col-span "2"} "----" (:name grouping) "----"]] + (for [account (:accounts grouping) + + :when [(not (dollars-0? (js/parseFloat (:amount account))))]] + + [: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 profit-and-loss-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 "Profit and Loss"] + [:div.field + [:p.help "From"] + [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 [:from-date] + :event [::date-picked] + :popper-props (clj->js {:placement "right"}) + :subscription params}]]] + + [:div.field + [:p.help "To"] + [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 [:to-date] + :event [::date-picked] + :popper-props (clj->js {:placement "right"}) + :subscription params}]]] + + [:h2.title "Assets"] + [grouping @(re-frame/subscribe [::assets])] + [:h2.title "Expenses"] + [grouping @(re-frame/subscribe [::expenses])] + [:h2.title "Liabilities"] + [grouping @(re-frame/subscribe [::liabilities])] + ])) + {:component-will-mount #(re-frame/dispatch-sync [::params-change {:from-date (date->str (t/minus (t/now) (t/period :years 1)) standard) + :to-date (date->str (t/now) standard)}]) })) (defn profit-and-loss-page [] [side-bar-layout