From 02696110b86fe38bd15fa288ab30347f313674b6 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Wed, 14 Dec 2022 08:29:16 -0800 Subject: [PATCH] ledger export --- src/clj/auto_ap/graphql.clj | 3 ++ src/clj/auto_ap/graphql/ledger.clj | 44 +++++++++++++++- src/cljs/auto_ap/views/pages/ledger.cljs | 51 ++++++++++++++++--- .../auto_ap/views/pages/ledger/table.cljs | 4 +- 4 files changed, 92 insertions(+), 10 deletions(-) diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 655588fa..b5cfa55a 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -236,6 +236,9 @@ :role {:type :role} :clients {:type '(list :client)}}} + :csv + {:fields {:csv_content_b64 {:type 'String}}} + :account_client_override {:fields {:id {:type :id} :client {:type :client} diff --git a/src/clj/auto_ap/graphql/ledger.clj b/src/clj/auto_ap/graphql/ledger.clj index b641d817..f3ce8ebd 100644 --- a/src/clj/auto_ap/graphql/ledger.clj +++ b/src/clj/auto_ap/graphql/ledger.clj @@ -4,6 +4,7 @@ [auto-ap.datomic.accounts :as a] [auto-ap.datomic.clients :as d-clients] [auto-ap.datomic.ledger :as l] + [auto-ap.time :as atime] [auto-ap.ledger.reports :as l-reports] [auto-ap.datomic.vendors :as d-vendors] [auto-ap.graphql.utils @@ -14,11 +15,13 @@ [clj-time.coerce :as coerce] [clj-time.core :as t] [clojure.tools.logging :as log] + [clojure.data.csv :as csv] [com.walmartlabs.lacinia.util :refer [attach-resolvers]] [datomic.api :as d] [mount.core :as mount] [unilog.context :as lc] - [yang.scheduler :as scheduler])) + [yang.scheduler :as scheduler]) + (:import [org.apache.commons.codec.binary Base64])) (mount/defstate running-balance-cache :start (atom {})) @@ -43,6 +46,38 @@ journal-entries)] (result->page journal-entries journal-entries-count :journal_entries (:filters args)))) +(defn get-ledger-csv [context args _] + (let [args (assoc args :id (:id context)) + [journal-entries journal-entries-count] (l/get-graphql (assoc (<-graphql (:filters args)) + :per-page Integer/MAX_VALUE + :id (:id context))) + + + ] + {:csv_content_b64 (Base64/encodeBase64String + (.getBytes + (with-open [w (java.io.StringWriter.)] + (csv/write-csv w + (into [["Client" "Vendor" "Date" "Journal Entry" "Journal Entry Line" "Account" "Debit" "Credit"]] + (->> journal-entries + (mapcat (fn [j] + (map + (fn [li] + [(-> j :journal-entry/client :client/code) + (-> j :journal-entry/vendor :vendor/name) + (atime/unparse (coerce/to-date-time (-> j :journal-entry/date)) + atime/normal-date) + (-> j :db/id) + (-> li :db/id) + (-> li :journal-entry-line/account :account/name) + (-> li :journal-entry-line/debit) + (-> li :journal-entry-line/credit) + ]) + (:journal-entry/line-items j)) + )))) + :quote? (constantly true)) + (.toString w))))})) + (defn roll-up-until ([lookup-account all-ledger-entries end-date] @@ -741,7 +776,11 @@ :ledger_page {:type :ledger_page :args {:filters {:type :ledger_filters}} - :resolve :get-ledger-page}}) + :resolve :get-ledger-page} + + :ledger_csv {:type :csv + :args {:filters {:type :ledger_filters}} + :resolve :get-ledger-csv}}) (def mutations {:import_ledger @@ -809,6 +848,7 @@ :profit-and-loss-pdf profit-and-loss-pdf :journal-detail-report-pdf journal-detail-report-pdf :balance-sheet-pdf balance-sheet-pdf + :get-ledger-csv get-ledger-csv :get-journal-detail-report get-journal-detail-report :mutation/delete-external-ledger delete-external-ledger :mutation/import-ledger import-ledger}) diff --git a/src/cljs/auto_ap/views/pages/ledger.cljs b/src/cljs/auto_ap/views/pages/ledger.cljs index 11d78de5..7650a20c 100644 --- a/src/cljs/auto_ap/views/pages/ledger.cljs +++ b/src/cljs/auto_ap/views/pages/ledger.cljs @@ -3,6 +3,7 @@ [auto-ap.subs :as subs] [auto-ap.views.components.layouts :refer [side-bar-layout]] [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.components.buttons :as buttons] [auto-ap.views.pages.ledger.side-bar :as side-bar :refer [ledger-side-bar]] @@ -12,7 +13,8 @@ [clojure.set :as set] [re-frame.core :as re-frame] [reagent.core :as reagent] - [vimsical.re-frame.fx.track :as track])) + [vimsical.re-frame.fx.track :as track] + [vimsical.re-frame.cofx.inject :as inject])) (defn data-params->query-params [params] {:start (:start params 0) @@ -31,7 +33,7 @@ (re-frame/reg-event-fx ::params-change [with-user] - (fn [{:keys [user]} [_ params]] + (fn [{:keys [user db]} [_ params]] {:graphql {:token user :owns-state {:single [::data-page/page ::page]} :query-obj {:venia/queries [[:ledger-page @@ -56,7 +58,30 @@ :end]]]} :on-success (fn [result] [::data-page/received ::page (set/rename-keys (:ledger-page result) - {:journal-entries :data})])}})) + {:journal-entries :data})])} + :db (dissoc db ::csv-content)})) + +(re-frame/reg-sub + ::csv-content + (fn [db] + (::csv-content db))) + +(re-frame/reg-event-fx + ::csv-exported + (fn [{:keys [db]} [_ csv]] + {:db (assoc db ::csv-content csv)})) + +(re-frame/reg-event-fx + ::export-csv + [with-user (re-frame/inject-cofx ::inject/sub [::data-page/params ::page])] + (fn [{:keys [user db] ::data-page/keys [params]}] + {:graphql {:token user + :owns-state {:single [::data-page/page ::page]} + :query-obj {:venia/queries [[:ledger-csv + {:filters (data-params->query-params params)} + [:csv_content_b64]]]} + :on-success (fn [result] + [::csv-exported (:csv-content-b64 (:ledger-csv result))])}})) (re-frame/reg-event-fx ::unmounted @@ -67,19 +92,33 @@ (re-frame/reg-event-fx ::mounted (fn [{:keys [db]} _] - {:db (assoc-in db [::data-page/settled-filters ::page :date-range] {:start (date->str (time/plus (time/now) (time/months -1)) - standard)}) + {:db (-> db (assoc-in [::data-page/settled-filters ::page :date-range] {:start (date->str (time/plus (time/now) (time/months -1)) + standard)}) + (assoc ::csv-content nil)) ::track/register {:id ::params :subscription [::data-page/params ::page] :event-fn (fn [params] [::params-change params])}})) +(defn action-buttons [] + (let [params @(re-frame/subscribe [::data-page/params ::page]) + csv-content @(re-frame/subscribe [::csv-content]) + is-admin? @(re-frame/subscribe [::subs/is-admin?])] + (when is-admin? + (if csv-content + [:a {:href (str "data:attachment/csv;base64," csv-content) + :target "_blank" + :download (str "ledger.csv")} + "Click here to download"] + [buttons/event-button {:event [::export-csv] + :name "Export"}])))) (defn ledger-content [] [:div [:h1.title "Ledger"] [table/table {:id :ledger - :data-page ::page}]]) + :data-page ::page + :action-buttons [action-buttons]}]]) (defn ledger-page [] diff --git a/src/cljs/auto_ap/views/pages/ledger/table.cljs b/src/cljs/auto_ap/views/pages/ledger/table.cljs index f5da3366..ae47bb05 100644 --- a/src/cljs/auto_ap/views/pages/ledger/table.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/table.cljs @@ -92,13 +92,13 @@ #_[grid/cell {:class "has-text-right"} (when running-balance (nf running-balance ))]])]]) -(defn table [{:keys [data-page]}] +(defn table [{:keys [data-page action-buttons]}] (let [{:keys [data]} @(re-frame/subscribe [::data-page/page data-page]) selected-client @(re-frame/subscribe [::subs/client]) bank-accounts-by-id @(re-frame/subscribe [::subs/bank-accounts-by-id])] [grid/grid {:data-page data-page :column-count (if selected-client 5 6)} - [grid/controls data] + [grid/controls (assoc data :action-buttons action-buttons)] [grid/table {:fullwidth true :class ["wrappable"]} [grid/header [grid/row {}