450 lines
25 KiB
Clojure
450 lines
25 KiB
Clojure
(ns auto-ap.routes.exports
|
|
(:require
|
|
[auto-ap.datomic :refer [conn pull-many]]
|
|
[auto-ap.datomic.accounts :as accounts]
|
|
[auto-ap.datomic.clients :as d-clients]
|
|
[auto-ap.datomic.transactions :as d-transactions]
|
|
[auto-ap.datomic.vendors :as vendor]
|
|
[auto-ap.graphql :as graphql]
|
|
[auto-ap.graphql.utils
|
|
:refer [->graphql <-graphql assert-admin assert-can-see-client]]
|
|
[auto-ap.routes.utils :refer [wrap-secure]]
|
|
[auto-ap.time :as atime]
|
|
[buddy.sign.jwt :as jwt]
|
|
[cheshire.generate :as generate]
|
|
[clj-time.coerce :as coerce :refer [to-date]]
|
|
[clj-time.core :as time]
|
|
[clojure.data.csv :as csv]
|
|
[clojure.edn :refer [read-string]]
|
|
[clojure.tools.logging :as log]
|
|
[com.unbounce.dogstatsd.core :as statsd]
|
|
[config.core :refer [env]]
|
|
[datomic.api :as d]
|
|
[datomic.client.api :as dc]
|
|
[ring.middleware.json :refer [wrap-json-response]]
|
|
[venia.core :as venia]))
|
|
|
|
(defn wrap-csv-response [handler]
|
|
(fn [request]
|
|
(let [response (handler request)]
|
|
(update response :body #(with-open [w (java.io.StringWriter.)]
|
|
(csv/write-csv w %)
|
|
(.toString w))))))
|
|
|
|
(defn aggregated-sales-export [{:keys [query-params]}]
|
|
(let [client-id (Long/parseLong (get query-params "client-id"))
|
|
identity (jwt/unsign (get query-params "key") (:jwt-secret env) {:alg :hs512})]
|
|
(assert-can-see-client identity client-id)
|
|
(into (list)
|
|
(dc/q {:query {:find '[?d4 (sum ?total) (sum ?tax) (sum ?tip) (sum ?service-charge)]
|
|
:in '[$ ?c]
|
|
:where '[[?s :sales-order/client ?c]
|
|
[?s :sales-order/date ?d]
|
|
[?s :sales-order/total ?total]
|
|
[?s :sales-order/tax ?tax]
|
|
[?s :sales-order/tip ?tip]
|
|
[?s :sales-order/service-charge ?service-charge]
|
|
[(clj-time.coerce/to-date-time ?d) ?d2]
|
|
[(auto-ap.time/localize ?d2) ?d3]
|
|
[(auto-ap.time/unparse ?d3 auto-ap.time/normal-date) ?d4]]}
|
|
:args [(dc/db conn) client-id]}))))
|
|
|
|
(defn client-tag [params]
|
|
(when-let [code (or (params "client-code")
|
|
(:client-code params))]
|
|
(str "client:" code)))
|
|
|
|
(defn datomic-map->graphql-map [m]
|
|
(into {} (map (fn [[k v]]
|
|
[(keyword (name k))
|
|
(cond (:db/ident v)
|
|
(str (keyword (name (:db/ident v))))
|
|
|
|
(map? v)
|
|
(datomic-map->graphql-map v)
|
|
|
|
(and (seqable? v) (map? (first v)))
|
|
(into [] (map datomic-map->graphql-map) v)
|
|
|
|
(inst? v)
|
|
(atime/unparse (clj-time.coerce/to-date-time v) atime/iso-date)
|
|
|
|
(= :db/id k)
|
|
(str v)
|
|
|
|
(double? v)
|
|
(str v)
|
|
|
|
|
|
:else
|
|
v)
|
|
]))
|
|
m))
|
|
|
|
(defn export-invoices [{:keys [query-params identity]}]
|
|
(assert-admin identity)
|
|
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
|
|
"export:invoice"}}]
|
|
{:body
|
|
(list (into (list)
|
|
(map datomic-map->graphql-map)
|
|
(map first (dc/q '[:find (pull ?i [:db/id :invoice/total :invoice/outstanding-balance :invoice/invoice-number :invoice/date :invoice/original-id
|
|
{ :invoice/status [:db/ident]
|
|
:invoice/payments
|
|
[:invoice-payment/amount
|
|
{:invoice-payment/payment [:payment/check-number
|
|
:payment/memo
|
|
{:payment/bank_account [:bank-account/id :bank-account/name :bank-account/number :bank-account/bank-name :bank-account/bank-code :bank-account/code]}]}]
|
|
:invoice/vendor [:vendor/name
|
|
:db/id
|
|
{:vendor/primary-contact [:contact/name]
|
|
:vendor/address [:address/street1 :address/city :address/state :address/zip]}]
|
|
:invoice/expense-accounts [:db/id
|
|
:invoice-expense-account/amount
|
|
:invoice-expense-account/id
|
|
:invoice-expense-account/location
|
|
{:invoice-expense-account/account
|
|
[:db/id :account/numeric-code :account/name]}]
|
|
:invoice/client [:client/name :db/id :client/code :client/locations]}])
|
|
:in $ ?c
|
|
:where [?i :invoice/client ?c]]
|
|
|
|
(dc/db conn)
|
|
[:client/code (query-params "client-code")]))))}))
|
|
|
|
(defn export-payments [{:keys [query-params identity]}]
|
|
(assert-admin identity)
|
|
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
|
|
"export:payment"}}]
|
|
(let [query [[:all_payments
|
|
{:client-code (query-params "client-code")
|
|
:original-id (query-params "original")}
|
|
[:id :check-number :amount :memo :date :status :type :original-id
|
|
[:invoices [[:invoice [:id :original-id]] :amount]]
|
|
[:bank-account [:number :code :bank-name :bank-code :id]]
|
|
[:vendor [:name :id [:primary-contact [:name :email :phone]] [:default-account [:name :numeric-code :id]] [:address [:street1 :city :state :zip]]]]
|
|
[:client [:id :name :code]]
|
|
]]]
|
|
payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))]
|
|
{:body
|
|
(list (:all-payments (:data payments)))})))
|
|
|
|
|
|
(defn export-sales [{:keys [query-params identity]}]
|
|
(assert-admin identity)
|
|
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
|
|
"export:sales"}}]
|
|
(let [query [[:all_sales_orders
|
|
(cond-> {:client-code (query-params "client-code")}
|
|
(query-params "after") (assoc :date-range {:start (query-params "after")
|
|
:end nil}))
|
|
[:id
|
|
:location
|
|
:external_id
|
|
:total
|
|
:tip
|
|
:tax
|
|
:discount
|
|
:returns
|
|
:service_charge
|
|
:date
|
|
[:charges [:type_name :total :tip]]
|
|
[:line_items [:item_name :total :tax :discount :category]]
|
|
[:client [:id :name :code]]]]]
|
|
payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))
|
|
parsedouble #(some-> % Double/parseDouble) ]
|
|
{:body
|
|
(seq (map
|
|
(fn [s]
|
|
(-> s
|
|
(assoc :utc_date (:date s))
|
|
(update :date (fn [d]
|
|
(coerce/to-string (coerce/to-local-date-time (time/to-time-zone (coerce/to-date-time d) (time/time-zone-for-id "America/Los_Angeles"))))))
|
|
(update :total parsedouble)
|
|
(update :tax parsedouble)
|
|
(update :discount parsedouble)
|
|
(update :tip parsedouble)
|
|
(update :line-items (fn [lis]
|
|
(map
|
|
(fn [li]
|
|
(-> li
|
|
(update :tax parsedouble)
|
|
(update :discount parsedouble)
|
|
(update :total parsedouble)))
|
|
lis)))
|
|
(update :charges (fn [charges]
|
|
(map
|
|
(fn [charge]
|
|
(-> charge
|
|
(update :tip parsedouble)
|
|
(update :total parsedouble)))
|
|
charges)))))
|
|
(:all-sales-orders (:data payments))))})))
|
|
|
|
(defn export-expected-deposits [{:keys [query-params identity]}]
|
|
(assert-admin identity)
|
|
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
|
|
"export:deposit"}}]
|
|
(let [query [[:all_expected_deposits
|
|
(cond-> {:client-code (query-params "client-code")}
|
|
(query-params "after") (assoc :date-range {:start (query-params "after")
|
|
:end nil}))
|
|
[:id
|
|
[:client [:id :name :code]]
|
|
:location
|
|
:external_id
|
|
:total
|
|
:fee
|
|
:date]]]
|
|
payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))]
|
|
{:body
|
|
(seq (map
|
|
(fn [d]
|
|
(-> d
|
|
(update :fee #(some-> % Double/parseDouble))
|
|
(update :total #(some-> % Double/parseDouble))))
|
|
(:all-expected-deposits (:data payments))))})))
|
|
|
|
|
|
(generate/add-encoder org.joda.time.DateTime
|
|
(fn [c jsonGenerator]
|
|
(.writeString jsonGenerator (str c))))
|
|
|
|
|
|
(defn export-clients[{:keys [identity]}]
|
|
(assert-admin identity)
|
|
{:body (into []
|
|
(map <-graphql)
|
|
(d-clients/get-all))})
|
|
|
|
(defn export-vendors [{:keys [identity]}]
|
|
(assert-admin identity)
|
|
(statsd/time! [(str "export.time") {:tags #{"export:vendors"}}]
|
|
{:body
|
|
(map <-graphql (->> (dc/q '[:find ?e
|
|
:in $
|
|
:where [?e :vendor/name]]
|
|
(dc/db conn))
|
|
(map first)
|
|
(pull-many (dc/db conn) vendor/default-read)))}))
|
|
|
|
(defn export-company-vendors [{:keys [identity query-params]}]
|
|
(statsd/time! [(str "export.time") {:tags #{"export:company-vendors"}}]
|
|
(let [client (:db/id (dc/pull (dc/db conn) [:db/id] [:client/code (get query-params "client")]))
|
|
|
|
_ (assert-can-see-client identity client)
|
|
data (->> (dc/q '[:find (pull ?v [:vendor/name
|
|
:vendor/terms
|
|
{:vendor/default-account [:account/name :account/numeric-code
|
|
{:account/client-overrides
|
|
[:account-client-override/client
|
|
:account-client-override/name]}]
|
|
:vendor/terms-overrides [:vendor-terms-override/client
|
|
:vendor-terms-override/terms]
|
|
:vendor/account-overrides [:vendor-account-override/client
|
|
{:vendor-account-override/account [:account/numeric-code :account/name
|
|
{:account/client-overrides
|
|
[:account-client-override/client
|
|
:account-client-override/name]}]}]
|
|
:vendor/address [:address/street1 :address/city :address/state :address/zip]}])
|
|
:in $ ?c
|
|
:where [?vu :vendor-usage/client ?c]
|
|
[?vu :vendor-usage/count ?count]
|
|
[(>= ?vu 0)]
|
|
[?vu :vendor-usage/vendor ?v]
|
|
(not [?v :vendor/hidden true])]
|
|
(dc/db conn)
|
|
client)
|
|
(map (fn [[v]]
|
|
[(-> v :vendor/name)
|
|
(-> v :vendor/address :address/street1)
|
|
(-> v :vendor/address :address/city)
|
|
(-> v :vendor/address :address/state)
|
|
(-> v :vendor/address :address/zip)
|
|
(-> v (vendor/terms-for-client-id client) )
|
|
(-> v (vendor/account-for-client-id client) (accounts/clientize client) :account/name)
|
|
(-> v (vendor/account-for-client-id client) :account/numeric-code)
|
|
]
|
|
))
|
|
(into [["Vendor Name" "Address" "City" "State" "Zip" "Terms" "Account" "Account Code"]]))]
|
|
{:body
|
|
(into (list)
|
|
data)})))
|
|
|
|
(defn export-ledger [{:keys [identity query-params]}]
|
|
(let [start-date (or (some-> (query-params "start-date")
|
|
(atime/parse atime/iso-date))
|
|
(time/plus (time/now) (time/days -120)))]
|
|
(log/info "exporting for " (query-params "client-code") "starting" start-date)
|
|
(assert-admin identity)
|
|
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
|
|
"export:ledger2"}}]
|
|
(let [results (->> (dc/q '[:find (pull ?e [:db/id
|
|
:journal-entry/external-id
|
|
:journal-entry/cleared
|
|
:journal-entry/alternate-description
|
|
:journal-entry/date
|
|
:journal-entry/note
|
|
:journal-entry/amount
|
|
:journal-entry/source
|
|
:journal-entry/cleared-against
|
|
:journal-entry/original-entity
|
|
{:journal-entry/client [:client/name :client/code :db/id]
|
|
:journal-entry/vendor [:vendor/name :db/id]
|
|
:journal-entry/line-items
|
|
[:db/id
|
|
:journal-entry-line/location
|
|
:journal-entry-line/debit
|
|
:journal-entry-line/credit
|
|
{:journal-entry-line/account
|
|
[:bank-account/include-in-reports
|
|
:bank-account/bank-name
|
|
:bank-account/numeric-code
|
|
:bank-account/code
|
|
:bank-account/visible
|
|
:bank-account/name
|
|
:bank-account/number
|
|
:account/code
|
|
:account/name
|
|
:account/numeric-code
|
|
:account/location
|
|
{:account/type [:db/ident :db/id]}
|
|
{:bank-account/type [:db/ident :db/id]}]}]}])
|
|
:in $ ?c ?start-date
|
|
:where [?e :journal-entry/client ?c]
|
|
[?e :journal-entry/date ?date]
|
|
[(>= ?date ?start-date)]]
|
|
(dc/db conn)
|
|
[:client/code (query-params "client-code")]
|
|
(coerce/to-date start-date)))
|
|
tf-result (transduce (comp
|
|
(map first)
|
|
(filter (fn [je]
|
|
(every?
|
|
(fn [jel]
|
|
(let [include-in-reports (-> jel :journal-entry-line/account :bank-account/include-in-reports)]
|
|
(or (nil? include-in-reports)
|
|
(true? include-in-reports))))
|
|
(:journal-entry/line-items je))))
|
|
(map <-graphql))
|
|
conj
|
|
(list)
|
|
results)]
|
|
{:body
|
|
tf-result}))))
|
|
|
|
(defn export-accounts [{:keys [query-params identity]}]
|
|
(assert-admin identity)
|
|
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
|
|
"export:accounts"}}]
|
|
(let [client-id (d-clients/code->id (query-params "client-code"))
|
|
query [[:all-accounts
|
|
[:id :numeric_code :type :applicability :location :name [:client_overrides [:name [:client [:id :code :name]]]]]]]
|
|
all-accounts (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))]
|
|
|
|
{:body
|
|
(list (transduce
|
|
(comp
|
|
(filter (fn [a]
|
|
(let [overriden-clients (set (map (comp :id :client) (:client-overrides a)))]
|
|
(or (nil? (:applicability a))
|
|
(= :global (:applicability a))
|
|
(overriden-clients (str client-id))))))
|
|
(map (fn [a]
|
|
(let [client->name (reduce
|
|
(fn [override co]
|
|
(assoc override (str (:id (:client co))) (:name co)))
|
|
{}
|
|
(:client-overrides a))]
|
|
(-> a
|
|
(assoc :global-name (:name a))
|
|
(assoc :client-name (client->name (str client-id) (:name a)))
|
|
(dissoc :client-overrides))))))
|
|
conj
|
|
(list)
|
|
(:all-accounts (:data all-accounts))))})))
|
|
|
|
(defn export-transactions2 [{:keys [query-params identity]}]
|
|
(assert-admin identity)
|
|
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
|
|
"export:transactions2"}}]
|
|
{:body (let [db (dc/db conn)]
|
|
(->>
|
|
(dc/q {:query {:find ['?e]
|
|
:in ['$ '?client-code]
|
|
:where ['[?e :transaction/client ?client-code]]}
|
|
:args [db [:client/code (query-params "client-code")]]})
|
|
(map first)
|
|
;; TODO
|
|
#_(map (fn [e]
|
|
(let [e (dc/entity db e)
|
|
client (:transaction/client e)
|
|
bank-account (:transaction/bank-account e)]
|
|
{:id (:db/id e)
|
|
:date (:transaction/date e)
|
|
:post_date (:transaction/post-date e)
|
|
:client { :code (:client/code client)
|
|
:id (:db/id client)
|
|
:name (:client/name client)}
|
|
:amount (:transaction/amount e)
|
|
:description_original (:transaction/description-original e)
|
|
:approval_status (:transaction/approval-status e)
|
|
:bank_account {:name (:bank-account/name bank-account)
|
|
:code (:bank-account/code bank-account)
|
|
:id (:db/id bank-account)}})))))}))
|
|
|
|
(defn export-transactions [{:keys [query-params identity]}]
|
|
(assert-admin identity)
|
|
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
|
|
"export:transactions"}}]
|
|
(let [[transactions] (d-transactions/get-graphql {:client-code (query-params "client-code")
|
|
#_#_:original-id (Integer/parseInt (query-params "original"))
|
|
:count Integer/MAX_VALUE})]
|
|
|
|
|
|
{:body (map
|
|
(comp ->graphql
|
|
(fn [i]
|
|
(cond-> i
|
|
true (update :transaction/date to-date)
|
|
true (update :transaction/post-date to-date)
|
|
(:transaction/payment i) (update-in [:transaction/payment :payment/date] to-date)
|
|
(:transaction/expected-deposit i) (update-in [:transaction/expected-deposit :expected-deposit/date] to-date))))
|
|
transactions)})))
|
|
|
|
(defn export-raw [{:keys [query-params identity]}]
|
|
(assert-admin identity)
|
|
(log/info "Executing raw query " (get query-params "query" ))
|
|
(statsd/time! [(str "export.time") {:tags #{"export:raw"}}]
|
|
{:body
|
|
(into (list) (apply dc/q (read-string (get query-params "query" )) (into [(dc/db conn)] (read-string (get query-params "args" "[]")))))}))
|
|
|
|
|
|
(def routes2 {"api/" {"sales/" {"aggregated/" {#"export/?" {:get :aggregated-sales-export}}
|
|
#"export/?" {:get :export-sales}}
|
|
"invoices/" {#"export/?" {:get :export-invoices}}
|
|
"payments/" {#"export/?" {:get :export-payments}}
|
|
"expected-deposit/" {#"export/?" {:get :export-expected-deposits}}
|
|
"clients/" {#"export/?" {:get :export-clients}}
|
|
"vendors/" {#"export/?" {:get :export-vendors}
|
|
"/company" {#"export/?" {:get :export-company-vendors}}}
|
|
"ledger/" {#"export/?" {:get :export-ledger}}
|
|
"accounts/" {#"export/?" {:get :export-accounts}}
|
|
"transactions/" {#"export/?" {:get :export-transactions}
|
|
#"export2/?" {:get :export-transactions2}}
|
|
#"raw/?" {:get :export-raw}}})
|
|
|
|
(def match->handler {:aggregated-sales-export (wrap-csv-response aggregated-sales-export)
|
|
:export-invoices (-> export-invoices wrap-json-response wrap-secure )
|
|
:export-payments (-> export-payments wrap-json-response wrap-secure)
|
|
:export-sales (-> export-sales wrap-json-response wrap-secure)
|
|
:export-expected-deposits (-> export-expected-deposits wrap-json-response wrap-secure)
|
|
:export-clients (-> export-clients wrap-json-response wrap-secure)
|
|
:export-vendors (-> export-vendors wrap-json-response wrap-secure)
|
|
:export-company-vendors (-> export-company-vendors wrap-csv-response wrap-secure)
|
|
:export-ledger (-> export-ledger wrap-json-response wrap-secure)
|
|
:export-accounts (-> export-accounts wrap-json-response wrap-secure)
|
|
:export-transactions (-> export-transactions wrap-json-response wrap-secure)
|
|
:export-transactions2 (-> export-transactions2 wrap-json-response wrap-secure)
|
|
:export-raw (-> export-raw wrap-json-response wrap-secure)})
|