Adds 1099 functionality, cleans up routing rules.
This commit is contained in:
@@ -13,8 +13,6 @@
|
||||
org.slf4j/log4j-over-slf4j
|
||||
org.slf4j/slf4j-nop
|
||||
org.slf4j/slf4j-log4j12]]
|
||||
[compojure "1.6.2" :exclusions [ring
|
||||
ring/ring-core]]
|
||||
[com.unbounce/clojure-dogstatsd-client "0.7.0"]
|
||||
[bidi "2.1.6"]
|
||||
[ring/ring-defaults "0.3.2" :exclusions [ring ring/ring-core]]
|
||||
|
||||
56
scratch-sessions/1099-gather.clj
Normal file
56
scratch-sessions/1099-gather.clj
Normal file
@@ -0,0 +1,56 @@
|
||||
;; This buffer is for Clojure experiments and evaluation.
|
||||
|
||||
;; Press C-j to evaluate the last expression.
|
||||
|
||||
;; You can also press C-u C-j to evaluate the expression and pretty-print its result.
|
||||
|
||||
|
||||
(user/init-repl)
|
||||
|
||||
(clojure.data.csv/write-csv *out*
|
||||
|
||||
(->> (d/q '[:find
|
||||
(pull ?c [:client/code])
|
||||
(pull ?v [:vendor/name
|
||||
{:vendor/legal-entity-1099-type [:db/ident]}
|
||||
{:vendor/legal-entity-tin-type [:db/ident]}
|
||||
{:vendor/address [:address/street1
|
||||
:address/city
|
||||
:address/state
|
||||
:address/zip]}
|
||||
:vendor/legal-entity-first-ein
|
||||
:vendor/legal-entity-first-name
|
||||
:vendor/legal-entity-middle-name
|
||||
:vendor/legal-entity-last-name])
|
||||
(sum ?a)
|
||||
:in $
|
||||
:where [?p :payment/date ?d ]
|
||||
[(>= ?d #inst "2022-01-01T08:00")]
|
||||
[(< ?d #inst "2023-01-01T08:00")]
|
||||
[?p :payment/client ?c]
|
||||
[?p :payment/amount ?a]
|
||||
[?p :payment/type :payment-type/check]
|
||||
[?p :payment/vendor ?v]]
|
||||
(d/db auto-ap.datomic/conn))
|
||||
(filter (fn [[_ _ a]]
|
||||
(>= a 600.0)))
|
||||
(map (fn [[client vendor amount]]
|
||||
[(:client/code client)
|
||||
(:vendor/name vendor)
|
||||
(some-> vendor :vendor/legal-entity-1099-type :db/ident name)
|
||||
(-> vendor :vendor/legal-entity-first-name)
|
||||
(-> vendor :vendor/legal-entity-middle-name)
|
||||
(-> vendor :vendor/legal-entity-last-name)
|
||||
(some-> vendor :vendor/legal-entity-tin-type :db/ident name)
|
||||
(-> vendor :vendor/legal-entity-tin)
|
||||
(-> vendor :vendor/address :address/street1)
|
||||
(-> vendor :vendor/address :address/street2)
|
||||
(-> vendor :vendor/address :address/city)
|
||||
(-> vendor :vendor/address :address/state)
|
||||
(-> vendor :vendor/address :address/zip)
|
||||
amount
|
||||
]))
|
||||
(sort )
|
||||
(into [["Client" "Vendor Name" "1099 Type" "First Name" "Middle Name" "Last Name" "TIN type" "TIN" "Street" "Street 2" "City" "State" "Zip"]]))
|
||||
:quote? (constantly true))
|
||||
|
||||
@@ -2,25 +2,25 @@
|
||||
(:require
|
||||
[amazonica.core :refer [defcredential]]
|
||||
[auto-ap.client-routes :as client-routes]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[auto-ap.logging :as alog]
|
||||
[auto-ap.routes.auth :as auth]
|
||||
[auto-ap.routes.exports :as exports]
|
||||
[auto-ap.routes.ezcater :as ezcater]
|
||||
[auto-ap.routes.graphql :as graphql]
|
||||
[auto-ap.routes.health :as health]
|
||||
[auto-ap.routes.invoices :as invoices]
|
||||
[auto-ap.routes.queries :as queries]
|
||||
[auto-ap.routes.yodlee2 :as yodlee2]
|
||||
[auto-ap.ssr.admin :as ssr-admin]
|
||||
[auto-ap.ssr.core :as ssr]
|
||||
[bidi.bidi :as bidi]
|
||||
[bidi.ring :refer [->ResourcesMaybe make-handler]]
|
||||
[buddy.auth.backends.session :refer [session-backend]]
|
||||
[buddy.auth.backends.token :refer [jws-backend]]
|
||||
[buddy.auth.middleware :refer [wrap-authentication wrap-authorization]]
|
||||
|
||||
[clojure.string :as str]
|
||||
[clojure.tools.logging :as log]
|
||||
[compojure.core :refer [ANY context defroutes GET routes]]
|
||||
[compojure.route :as route]
|
||||
[com.brunobonacci.mulog :as mu]
|
||||
[config.core :refer [env]]
|
||||
[mount.core :as mount]
|
||||
[ring.middleware.edn :refer [wrap-edn-params]]
|
||||
[ring.middleware.multipart-params :as mp]
|
||||
[ring.middleware.params :refer [wrap-params]]
|
||||
@@ -28,85 +28,153 @@
|
||||
[ring.middleware.session :refer [wrap-session]]
|
||||
[ring.middleware.session.cookie :refer [cookie-store]]
|
||||
[ring.util.response :as response]
|
||||
[unilog.context :as lc]))
|
||||
[unilog.context :as lc]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clj-time.core :as time]))
|
||||
|
||||
(when (:aws-access-key-id env)
|
||||
(defcredential (:aws-access-key-id env) (:aws-secret-access-key env) (:aws-region env)))
|
||||
|
||||
(def running? (atom false))
|
||||
(defn deep-merge [v & vs]
|
||||
(letfn [(rec-merge [v1 v2]
|
||||
(if (and (map? v1) (map? v2))
|
||||
(merge-with deep-merge v1 v2)
|
||||
v2))]
|
||||
(when (some identity vs)
|
||||
(reduce #(rec-merge %1 %2) v vs))))
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(mount/defstate manage-running?
|
||||
:start (reset! running? true)
|
||||
:stop (reset! running? false))
|
||||
(def all-routes ["/" (-> (into []
|
||||
(deep-merge ssr-routes/routes
|
||||
(second client-routes/routes)
|
||||
graphql/routes
|
||||
ezcater/routes
|
||||
health/routes
|
||||
queries/routes2
|
||||
yodlee2/routes
|
||||
auth/routes
|
||||
invoices/routes
|
||||
exports/routes2))
|
||||
(conj ["" (->ResourcesMaybe {:prefix "public/"})])
|
||||
(conj [true :not-found])) ;; always go for not found as last resort, have to switch to vec in order for that to work
|
||||
])
|
||||
|
||||
(defroutes static-routes
|
||||
(GET "/" [] (response/resource-response "index.html" {:root "public"}))
|
||||
|
||||
(route/resources "/")
|
||||
(routes (ANY "*" {:keys [uri]}
|
||||
(if (bidi/match-route client-routes/routes uri)
|
||||
(response/resource-response "index.html" {:root "public"})
|
||||
{:status 404
|
||||
:body "Not found"}))))
|
||||
(defn not-found [_]
|
||||
{:status 404
|
||||
:headers {}
|
||||
:body ""})
|
||||
|
||||
(defroutes health-check
|
||||
(GET "/health-check" []
|
||||
(if @running?
|
||||
{:status 200
|
||||
:body "Ok"}
|
||||
{:status 503
|
||||
:body "Application shut down"})))
|
||||
(defn render-index [_]
|
||||
(response/resource-response "index.html" {:root "public"}))
|
||||
|
||||
(def match->handler-lookup
|
||||
(-> {:not-found not-found}
|
||||
(merge ssr/key->handler)
|
||||
(merge graphql/match->handler)
|
||||
(merge ezcater/match->handler)
|
||||
(merge health/match->handler)
|
||||
(merge queries/match->handler)
|
||||
(merge yodlee2/match->handler)
|
||||
(merge auth/match->handler)
|
||||
(merge invoices/match->handler)
|
||||
(merge exports/match->handler)
|
||||
(merge
|
||||
(into {}
|
||||
(map
|
||||
|
||||
(fn [k]
|
||||
[k render-index])
|
||||
client-routes/all-matches)))))
|
||||
|
||||
(def match->handler
|
||||
(fn [route]
|
||||
(or (get match->handler-lookup route)
|
||||
route)))
|
||||
|
||||
|
||||
(def route-handler
|
||||
(make-handler all-routes
|
||||
match->handler))
|
||||
|
||||
(defn wrap-guess-route [handler]
|
||||
(fn [{:keys [uri request-method] :as request} ]
|
||||
(let [matched-route (:handler
|
||||
(bidi.bidi/match-route all-routes
|
||||
uri
|
||||
:request-method request-method))]
|
||||
(handler (assoc request
|
||||
:matched-route
|
||||
matched-route)))))
|
||||
|
||||
(defn test-match-route [method uri]
|
||||
(bidi.bidi/match-route all-routes
|
||||
uri
|
||||
:request-method method))
|
||||
|
||||
(defroutes api-routes
|
||||
(context "/api" []
|
||||
exports/export-routes
|
||||
yodlee2/routes
|
||||
queries/query2-routes
|
||||
invoices/routes
|
||||
graphql/routes
|
||||
ezcater/routes
|
||||
auth/routes
|
||||
health-check))
|
||||
|
||||
(def auth-backend (jws-backend {:secret (:jwt-secret env) :options {:alg :hs512}}))
|
||||
|
||||
(defn wrap-transaction [handler]
|
||||
(fn [request]
|
||||
(handler request)))
|
||||
|
||||
(def app-routes
|
||||
(routes
|
||||
(wrap-transaction api-routes)
|
||||
ssr-admin/admin-routes
|
||||
static-routes))
|
||||
|
||||
(defn wrap-logging [handler]
|
||||
(fn [request]
|
||||
(lc/with-context {:uri (:uri request)
|
||||
:source "request"
|
||||
(mu/with-context {:uri (:uri request)
|
||||
:query (:uri request)
|
||||
:request-method (:request-method request)
|
||||
:user (:identity request)
|
||||
:user-role (:user/role (:identity request))
|
||||
:user-name (:user/name (:identity request))}
|
||||
(mu/trace ::http-request-trace
|
||||
[]
|
||||
(lc/with-context {:uri (:uri request)
|
||||
:source "request"
|
||||
:user-role (:user/role (:identity request))
|
||||
:user-name (:user/name (:identity request))}
|
||||
|
||||
|
||||
(when-not (str/includes? (:uri request) "health-check")
|
||||
(log/info "Beginning request" (:uri request)))
|
||||
(handler request))))
|
||||
(when-not (str/includes? (:uri request) "health-check")
|
||||
(alog/info ::http-request-starting))
|
||||
(try
|
||||
(let [response (handler request)]
|
||||
(alog/info ::http-request-done
|
||||
:status-code (:status response))
|
||||
response)
|
||||
(catch Exception e
|
||||
(alog/error ::request-error
|
||||
:exception e)
|
||||
(throw e))))))))
|
||||
|
||||
(defn wrap-idle-session-timeout
|
||||
[handler ]
|
||||
(fn [request]
|
||||
(let [session (:session request {})
|
||||
end-time (coerce/to-date-time (::idle-timeout session))]
|
||||
(if (and end-time (time/before? end-time (time/now)))
|
||||
{:session nil
|
||||
:status 302
|
||||
:headers {"Location" "/login"}}
|
||||
(when-let [response (handler request)]
|
||||
(let [session (:session response session)]
|
||||
(if (nil? session)
|
||||
response
|
||||
(let [end-time (time/plus (time/now) (time/days 2))]
|
||||
(assoc response :session (assoc session ::idle-timeout (coerce/to-date end-time)))))))))))
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(def app
|
||||
(-> #'app-routes
|
||||
(wrap-logging)
|
||||
(-> route-handler
|
||||
(wrap-guess-route)
|
||||
(wrap-authorization auth-backend
|
||||
)
|
||||
(wrap-authentication auth-backend
|
||||
(session-backend {:authfn (fn [auth]
|
||||
(dissoc auth :exp))}))
|
||||
|
||||
(wrap-session {:store (ring.middleware.session.cookie/cookie-store
|
||||
(wrap-idle-session-timeout)
|
||||
(wrap-session {:store (cookie-store
|
||||
{:key
|
||||
(byte-array
|
||||
[42, 52, -31, 105, -126, -33, -118, -69, -82, -59, -15, -69, -38, 103, -102, -1])} )})
|
||||
(wrap-reload)
|
||||
(wrap-params)
|
||||
(mp/wrap-multipart-params)
|
||||
(wrap-edn-params)))
|
||||
(wrap-edn-params)
|
||||
(wrap-logging)))
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(ns auto-ap.routes.auth
|
||||
(:require [auto-ap.datomic.users :as users]
|
||||
[buddy.sign.jwt :as jwt]
|
||||
[clj-http.client :as http]
|
||||
[clj-time.core :as time]
|
||||
[compojure.core :refer [GET defroutes]]
|
||||
[config.core :refer [env]]
|
||||
[clojure.tools.logging :as log]))
|
||||
(:require
|
||||
[auto-ap.datomic.users :as users]
|
||||
[buddy.sign.jwt :as jwt]
|
||||
[clj-http.client :as http]
|
||||
[clj-time.core :as time]
|
||||
[clojure.tools.logging :as log]
|
||||
[config.core :refer [env]]))
|
||||
|
||||
(def google-client-id "264081895820-0nndcfo3pbtqf30sro82vgq5r27h8736.apps.googleusercontent.com")
|
||||
(def google-client-secret "OC-WemHurPXYpuIw5cT-B90g")
|
||||
@@ -19,48 +19,50 @@
|
||||
(:jwt-secret env)
|
||||
{:alg :hs512}))
|
||||
|
||||
(defroutes routes
|
||||
(GET "/oauth" {{:strs [code]} :query-params {:strs [host]} :headers}
|
||||
(try
|
||||
(let [auth (-> "https://accounts.google.com/o/oauth2/token"
|
||||
(http/post
|
||||
{:form-params {"client_id" google-client-id
|
||||
"client_secret" google-client-secret
|
||||
"code" code
|
||||
"redirect_uri" (str (:scheme env) "://" host "/api/oauth")
|
||||
"grant_type" "authorization_code"}
|
||||
:as :json})
|
||||
:body)
|
||||
token (:access_token auth)
|
||||
profile (-> (http/get "https://www.googleapis.com/oauth2/v1/userinfo"
|
||||
{:headers {"Authorization" (str "Bearer " token)} :as :json})
|
||||
:body)
|
||||
user (users/find-or-insert! {:user/provider "google"
|
||||
:user/provider-id (:id profile)
|
||||
:user/role :user-role/none
|
||||
:user/name (:name profile)})
|
||||
auth {:user (:name profile)
|
||||
:exp (time/plus (time/now) (time/days 30))
|
||||
:user/clients (map (fn [c]
|
||||
(select-keys c [:client/code :db/id :client/name :client/locations]))
|
||||
(:user/clients user))
|
||||
:user/role (name (:user/role user))
|
||||
:user/name (:name profile)}
|
||||
]
|
||||
(log/info "authenticated as user" user)
|
||||
;; TODO - these namespaces are not being transmitted/deserialized properly
|
||||
|
||||
(if (and token user)
|
||||
(let [jwt (jwt/sign auth
|
||||
(:jwt-secret env)
|
||||
{:alg :hs512})]
|
||||
|
||||
{:status 301
|
||||
:headers {"Location" (str "/?jwt=" jwt)}
|
||||
:session {:identity (dissoc auth :exp)}})
|
||||
{:status 401
|
||||
:body "Couldn't authenticate"}))
|
||||
(catch Exception e
|
||||
(log/warn e )
|
||||
{:status 401
|
||||
:body (str "Couldn't authenticate " (.toString e))}))))
|
||||
(defn oauth [{{:strs [code]} :query-params {:strs [host]} :headers}]
|
||||
(try
|
||||
(let [auth (-> "https://accounts.google.com/o/oauth2/token"
|
||||
(http/post
|
||||
{:form-params {"client_id" google-client-id
|
||||
"client_secret" google-client-secret
|
||||
"code" code
|
||||
"redirect_uri" (str (:scheme env) "://" host "/api/oauth")
|
||||
"grant_type" "authorization_code"}
|
||||
:as :json})
|
||||
:body)
|
||||
token (:access_token auth)
|
||||
profile (-> (http/get "https://www.googleapis.com/oauth2/v1/userinfo"
|
||||
{:headers {"Authorization" (str "Bearer " token)} :as :json})
|
||||
:body)
|
||||
user (users/find-or-insert! {:user/provider "google"
|
||||
:user/provider-id (:id profile)
|
||||
:user/role :user-role/none
|
||||
:user/name (:name profile)})
|
||||
auth {:user (:name profile)
|
||||
:exp (time/plus (time/now) (time/days 30))
|
||||
:user/clients (map (fn [c]
|
||||
(select-keys c [:client/code :db/id :client/name :client/locations]))
|
||||
(:user/clients user))
|
||||
:user/role (name (:user/role user))
|
||||
:user/name (:name profile)}
|
||||
]
|
||||
(log/info "authenticated as user" user)
|
||||
;; TODO - these namespaces are not being transmitted/deserialized properly
|
||||
|
||||
(if (and token user)
|
||||
(let [jwt (jwt/sign auth
|
||||
(:jwt-secret env)
|
||||
{:alg :hs512})]
|
||||
|
||||
{:status 301
|
||||
:headers {"Location" (str "/?jwt=" jwt)}
|
||||
:session {:identity (dissoc auth :exp)}})
|
||||
{:status 401
|
||||
:body "Couldn't authenticate"}))
|
||||
(catch Exception e
|
||||
(log/warn e )
|
||||
{:status 401
|
||||
:body (str "Couldn't authenticate " (.toString e))})))
|
||||
|
||||
(def routes {"api" {"/oauth" :oauth}})
|
||||
(def match->handler {:oauth oauth})
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
[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]
|
||||
[compojure.core :refer [context defroutes GET routes wrap-routes]]
|
||||
[config.core :refer [env]]
|
||||
[datomic.api :as d]
|
||||
[ring.middleware.json :refer [wrap-json-response]]
|
||||
@@ -30,25 +30,25 @@
|
||||
(csv/write-csv w %)
|
||||
(.toString w))))))
|
||||
|
||||
(def api-key-authed-routes
|
||||
(context "/" []
|
||||
(GET "/sales/aggregated/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)
|
||||
(d/query {: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 [(d/db conn) client-id]}))))))
|
||||
(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)
|
||||
(d/query {: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 [(d/db conn) client-id]}))))
|
||||
|
||||
|
||||
|
||||
(defn client-tag [params]
|
||||
(when-let [code (or (params "client-code")
|
||||
@@ -82,332 +82,366 @@
|
||||
]))
|
||||
m))
|
||||
|
||||
(def admin-only-routes
|
||||
(context "/" []
|
||||
(GET "/invoices/export" {:keys [query-params identity]}
|
||||
(assert-admin identity)
|
||||
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
|
||||
"export:invoice"}}]
|
||||
(list (into (list)
|
||||
(map datomic-map->graphql-map)
|
||||
(d/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]]
|
||||
(d/db conn)
|
||||
[:client/code (query-params "client-code")])))))
|
||||
(GET "/payments/export" {: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)}))]
|
||||
(list (:all-payments (:data payments))))))
|
||||
(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)
|
||||
(d/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]]
|
||||
(d/db conn)
|
||||
[:client/code (query-params "client-code")])))}))
|
||||
|
||||
(GET "/sales/export" {: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) ]
|
||||
(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))))))
|
||||
)
|
||||
|
||||
|
||||
|
||||
(GET "/expected-deposit/export" {: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)}))]
|
||||
(seq (map
|
||||
(fn [d]
|
||||
(-> d
|
||||
(update :fee #(some-> % Double/parseDouble))
|
||||
(update :total #(some-> % Double/parseDouble))))
|
||||
(:all-expected-deposits (:data payments)))))))
|
||||
(GET "/clients/export" {:keys [identity]}
|
||||
(assert-admin identity)
|
||||
(map <-graphql (d-clients/get-all)))
|
||||
|
||||
(GET "/vendors/export" {:keys [identity]}
|
||||
(assert-admin identity)
|
||||
(statsd/time! [(str "export.time") {:tags #{"export:vendors"}}]
|
||||
(map <-graphql (->> (d/q '[:find [?e ...]
|
||||
:in $
|
||||
:where [?e :vendor/name]]
|
||||
(d/db conn))
|
||||
(d/pull-many (d/db conn) vendor/default-read)))))
|
||||
|
||||
(GET "/vendors/company/export" {:keys [identity query-params]}
|
||||
(statsd/time! [(str "export.time") {:tags #{"export:company-vendors"}}]
|
||||
(let [client (:db/id (d/pull (d/db conn) [:db/id] [:client/code (get query-params "client")]))
|
||||
|
||||
_ (assert-can-see-client identity client)
|
||||
data (->> (d/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])]
|
||||
(d/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
|
||||
(with-open [w (java.io.StringWriter.)]
|
||||
(csv/write-csv w data
|
||||
:quote? (constantly true))
|
||||
(.toString w))
|
||||
:headers {"Content-Type" "application/csv"}})))
|
||||
(GET "/ledger/export" {: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 (->> (d/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)]]
|
||||
(d/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)]
|
||||
tf-result))))
|
||||
(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))))})))
|
||||
|
||||
(GET "/accounts/export" {: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)}))]
|
||||
(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))))})))
|
||||
|
||||
(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)))))))
|
||||
|
||||
(GET "/transactions/export" {: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})]
|
||||
|
||||
|
||||
(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)))
|
||||
)
|
||||
(generate/add-encoder org.joda.time.DateTime
|
||||
(fn [c jsonGenerator]
|
||||
(.writeString jsonGenerator (str c))))
|
||||
|
||||
(GET "/transactions/export2" {:keys [query-params identity]}
|
||||
(assert-admin identity)
|
||||
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
|
||||
"export:transactions2"}}]
|
||||
(let [db (d/db conn)]
|
||||
(->>
|
||||
(d/query {:query {:find ['?e]
|
||||
:in ['$ '?client-code]
|
||||
:where ['[?e :transaction/client ?client-code]]}
|
||||
:args [db [:client/code (query-params "client-code")]]})
|
||||
(map first)
|
||||
(map (fn [e]
|
||||
(let [e (d/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)}})))))))
|
||||
|
||||
(GET "/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"}}]
|
||||
(into (list) (apply d/q (read-string (get query-params "query" )) (into [(d/db conn)] (read-string (get query-params "args" "[]")))))))))
|
||||
(defn export-clients[{:keys [identity]}]
|
||||
(assert-admin identity)
|
||||
{:body (into []
|
||||
(map <-graphql)
|
||||
(d-clients/get-all))})
|
||||
|
||||
(defroutes export-routes
|
||||
(routes
|
||||
(wrap-routes api-key-authed-routes
|
||||
wrap-csv-response)
|
||||
(defn export-vendors [{:keys [identity]}]
|
||||
(assert-admin identity)
|
||||
(statsd/time! [(str "export.time") {:tags #{"export:vendors"}}]
|
||||
{:body
|
||||
(map <-graphql (->> (d/q '[:find [?e ...]
|
||||
:in $
|
||||
:where [?e :vendor/name]]
|
||||
(d/db conn))
|
||||
(d/pull-many (d/db conn) vendor/default-read)))}))
|
||||
|
||||
(wrap-routes (wrap-routes admin-only-routes wrap-secure)
|
||||
wrap-json-response)))
|
||||
(defn export-company-vendors [{:keys [identity query-params]}]
|
||||
(statsd/time! [(str "export.time") {:tags #{"export:company-vendors"}}]
|
||||
(let [client (:db/id (d/pull (d/db conn) [:db/id] [:client/code (get query-params "client")]))
|
||||
|
||||
_ (assert-can-see-client identity client)
|
||||
data (->> (d/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])]
|
||||
(d/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 (->> (d/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)]]
|
||||
(d/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 (d/db conn)]
|
||||
(->>
|
||||
(d/query {:query {:find ['?e]
|
||||
:in ['$ '?client-code]
|
||||
:where ['[?e :transaction/client ?client-code]]}
|
||||
:args [db [:client/code (query-params "client-code")]]})
|
||||
(map first)
|
||||
(map (fn [e]
|
||||
(let [e (d/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 d/q (read-string (get query-params "query" )) (into [(d/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)})
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
(ns auto-ap.routes.ezcater
|
||||
(:require
|
||||
[clojure.tools.logging :as log]
|
||||
[compojure.core :refer [context defroutes GET POST wrap-routes]]
|
||||
[ring.middleware.json :refer [wrap-json-params]]
|
||||
[auto-ap.ezcater.core :as e]
|
||||
[ring.util.request :refer [body-string]]))
|
||||
[auto-ap.logging :as alog]
|
||||
[ring.middleware.json :refer [wrap-json-params]]))
|
||||
|
||||
(defroutes routes
|
||||
(wrap-routes
|
||||
(context "/ezcater" []
|
||||
(GET "/event" request
|
||||
(log/info (str "GET EVENT " (body-string request) request))
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/json"}
|
||||
:body "{}"})
|
||||
(POST "/event" request
|
||||
(log/info (str "POST EVENT " (body-string request) request))
|
||||
(e/import-order (:json-params request))
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/json"}
|
||||
:body "{}"}))
|
||||
wrap-json-params))
|
||||
(defn handle-ezcater [{:keys [request-method json-params] :as r}]
|
||||
(cond
|
||||
(= :get request-method)
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/json"}
|
||||
:body "{}"}
|
||||
|
||||
(= :post request-method)
|
||||
(do
|
||||
(alog/info ::ezcater-request
|
||||
:json-params json-params)
|
||||
(e/import-order json-params)
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/json"}
|
||||
:body "{}"})
|
||||
|
||||
:else
|
||||
{:status 404}))
|
||||
|
||||
|
||||
|
||||
(def routes {"api/" {"ezcater/" {#"event/?" :ezcater-event}}})
|
||||
(def match->handler {:ezcater-event (-> handle-ezcater
|
||||
wrap-json-params)})
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
[auto-ap.logging :refer [warn-event]]
|
||||
[buddy.auth :refer [throw-unauthorized]]
|
||||
[clojure.edn :as edn]
|
||||
[compojure.core :refer [GET POST context defroutes
|
||||
wrap-routes]]
|
||||
[clojure.tools.logging :as log]))
|
||||
(defn handle-graphql [{:keys [request-method query-params] :as r}]
|
||||
(when (= "none" (:user/role (:identity r)))
|
||||
@@ -39,9 +37,5 @@
|
||||
:headers {"Content-Type" "application/edn"}}))))))
|
||||
|
||||
|
||||
(defroutes routes
|
||||
(wrap-routes
|
||||
(context "/graphql" []
|
||||
(GET "/" x (handle-graphql x))
|
||||
(POST "/" x (handle-graphql x)))
|
||||
wrap-secure))
|
||||
(def routes {"api/" {#"graphql/?" :graphql}})
|
||||
(def match->handler {:graphql (wrap-secure handle-graphql)})
|
||||
|
||||
19
src/clj/auto_ap/routes/health.clj
Normal file
19
src/clj/auto_ap/routes/health.clj
Normal file
@@ -0,0 +1,19 @@
|
||||
(ns auto-ap.routes.health
|
||||
(:require [mount.core :as mount]))
|
||||
|
||||
(def running? (atom false))
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(mount/defstate manage-running?
|
||||
:start (reset! running? true)
|
||||
:stop (reset! running? false))
|
||||
|
||||
(defn health-check [request]
|
||||
(if @running?
|
||||
{:status 200
|
||||
:body "Ok"}
|
||||
{:status 503
|
||||
:body "Application shut down"}))
|
||||
|
||||
(def routes {"api/" {"health-check" :health}})
|
||||
(def match->handler {:health health-check})
|
||||
@@ -18,7 +18,6 @@
|
||||
[clojure.java.io :as io]
|
||||
[clojure.string :as str]
|
||||
[clojure.tools.logging :as log]
|
||||
[compojure.core :refer [context defroutes POST wrap-routes]]
|
||||
[config.core :refer [env]]
|
||||
[datomic.api :as d]
|
||||
[digest]
|
||||
@@ -385,142 +384,147 @@
|
||||
rows)]
|
||||
(transact-with-ledger txes nil)))
|
||||
|
||||
(defroutes routes
|
||||
(wrap-routes
|
||||
(context "/" []
|
||||
(context "/transactions" []
|
||||
(POST "/batch-upload"
|
||||
{{:keys [data]} :edn-params user :identity}
|
||||
(assert-admin user)
|
||||
(try
|
||||
(let [stats (manual/import-batch (manual/tabulate-data data) (:user/name user))]
|
||||
{:status 200
|
||||
:body (pr-str stats)
|
||||
:headers {"Content-Type" "application/edn"}})
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 500
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)
|
||||
:data (ex-data e)})
|
||||
:headers {"Content-Type" "application/edn"}}))))
|
||||
(defn batch-upload-transactions [{{:keys [data]} :edn-params user :identity}]
|
||||
(assert-admin user)
|
||||
(try
|
||||
(let [stats (manual/import-batch (manual/tabulate-data data) (:user/name user))]
|
||||
{:status 200
|
||||
:body (pr-str stats)
|
||||
:headers {"Content-Type" "application/edn"}})
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 500
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)
|
||||
:data (ex-data e)})
|
||||
:headers {"Content-Type" "application/edn"}})))
|
||||
|
||||
(context "/invoices" []
|
||||
(POST "/upload"
|
||||
{{files :file
|
||||
files-2 "file"
|
||||
client :client
|
||||
client-2 "client"
|
||||
location :location
|
||||
location-2 "location"
|
||||
vendor :vendor
|
||||
vendor-2 "vendor"} :params
|
||||
user :identity}
|
||||
(let [files (or files files-2)
|
||||
client (or client client-2)
|
||||
location (or location location-2)
|
||||
vendor (some-> (or vendor vendor-2)
|
||||
(Long/parseLong))
|
||||
{:keys [filename tempfile]} files]
|
||||
(lc/with-context {:parsing-file filename}
|
||||
(log/info tempfile)
|
||||
(try
|
||||
(let [extension (last (str/split (.getName (io/file filename)) #"\."))
|
||||
s3-location (str "invoice-files/" (str (UUID/randomUUID)) "." extension)
|
||||
_ (s3/put-object (:data-bucket env)
|
||||
s3-location
|
||||
(io/input-stream tempfile)
|
||||
{:content-type (if (= "csv" extension)
|
||||
"text/csv"
|
||||
"application/pdf")
|
||||
:content-length (.length tempfile)})
|
||||
imports (->> (parse/parse-file (.getPath tempfile) filename)
|
||||
(map #(assoc %
|
||||
:client-override client
|
||||
:location-override location
|
||||
:vendor-override vendor
|
||||
:source-url (str "http://" (:data-bucket env)
|
||||
".s3-website-us-east-1.amazonaws.com/"
|
||||
s3-location))))]
|
||||
(import-uploaded-invoice user imports))
|
||||
{:status 200
|
||||
:body (pr-str {})
|
||||
:headers {"Content-Type" "application/edn"}}
|
||||
(catch Exception e
|
||||
(log/warn e)
|
||||
{:status 400
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)
|
||||
:data (ex-data e)})
|
||||
:headers {"Content-Type" "application/edn"}})))))
|
||||
(defn upload-invoices [{{files :file
|
||||
files-2 "file"
|
||||
client :client
|
||||
client-2 "client"
|
||||
location :location
|
||||
location-2 "location"
|
||||
vendor :vendor
|
||||
vendor-2 "vendor"} :params
|
||||
user :identity}]
|
||||
(let [files (or files files-2)
|
||||
client (or client client-2)
|
||||
location (or location location-2)
|
||||
vendor (some-> (or vendor vendor-2)
|
||||
(Long/parseLong))
|
||||
{:keys [filename tempfile]} files]
|
||||
(lc/with-context {:parsing-file filename}
|
||||
(log/info tempfile)
|
||||
(try
|
||||
(let [extension (last (str/split (.getName (io/file filename)) #"\."))
|
||||
s3-location (str "invoice-files/" (str (UUID/randomUUID)) "." extension)
|
||||
_ (s3/put-object (:data-bucket env)
|
||||
s3-location
|
||||
(io/input-stream tempfile)
|
||||
{:content-type (if (= "csv" extension)
|
||||
"text/csv"
|
||||
"application/pdf")
|
||||
:content-length (.length tempfile)})
|
||||
imports (->> (parse/parse-file (.getPath tempfile) filename)
|
||||
(map #(assoc %
|
||||
:client-override client
|
||||
:location-override location
|
||||
:vendor-override vendor
|
||||
:source-url (str "http://" (:data-bucket env)
|
||||
".s3-website-us-east-1.amazonaws.com/"
|
||||
s3-location))))]
|
||||
(import-uploaded-invoice user imports))
|
||||
{:status 200
|
||||
:body (pr-str {})
|
||||
:headers {"Content-Type" "application/edn"}}
|
||||
(catch Exception e
|
||||
(log/warn e)
|
||||
{:status 400
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)
|
||||
:data (ex-data e)})
|
||||
:headers {"Content-Type" "application/edn"}})))))
|
||||
|
||||
(POST "/upload-integreat"
|
||||
{{:keys [excel-rows]} :edn-params user :identity}
|
||||
(assert-admin user)
|
||||
(let [parsed-invoice-rows (parse-invoice-rows excel-rows)
|
||||
existing-rows (set (d-invoices/get-existing-set))
|
||||
grouped-rows (group-by
|
||||
(fn [i]
|
||||
(cond (seq (:errors i))
|
||||
:error
|
||||
(defn bulk-upload-invoices [{{:keys [excel-rows]} :edn-params user :identity}]
|
||||
(assert-admin user)
|
||||
(let [parsed-invoice-rows (parse-invoice-rows excel-rows)
|
||||
existing-rows (set (d-invoices/get-existing-set))
|
||||
grouped-rows (group-by
|
||||
(fn [i]
|
||||
(cond (seq (:errors i))
|
||||
:error
|
||||
|
||||
(existing-rows [(:vendor-id i) (:client-id i) (:invoice-number i)])
|
||||
:exists
|
||||
(existing-rows [(:vendor-id i) (:client-id i) (:invoice-number i)])
|
||||
:exists
|
||||
|
||||
:else
|
||||
:new))
|
||||
parsed-invoice-rows)
|
||||
vendors-not-found (->> parsed-invoice-rows
|
||||
(filter #(and (nil? (:vendor-id %))
|
||||
(not= "Cash" (:check %))))
|
||||
(map :vendor-name)
|
||||
set)
|
||||
_ (transact-with-ledger (invoice-rows->transaction (:new grouped-rows)
|
||||
user)
|
||||
user)]
|
||||
{:status 200
|
||||
:body (pr-str {:imported (count (:new grouped-rows))
|
||||
:already-imported (count (:exists grouped-rows))
|
||||
:vendors-not-found vendors-not-found
|
||||
:errors (map #(dissoc % :date) (:error grouped-rows))})
|
||||
:headers {"Content-Type" "application/edn"}})))
|
||||
(POST "/transactions/cleared-against"
|
||||
{{files :file
|
||||
files-2 "file"} :params
|
||||
user :identity}
|
||||
(let [files (or files files-2)
|
||||
{:keys [tempfile]} files]
|
||||
(assert-admin user)
|
||||
(try
|
||||
(import-transactions-cleared-against (.getPath tempfile))
|
||||
{:status 200
|
||||
:body (pr-str {})
|
||||
:headers {"Content-Type" "application/edn"}}
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 500
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)
|
||||
:data (ex-data e)})
|
||||
:headers {"Content-Type" "application/edn"}}))))
|
||||
(wrap-json-response (POST "/account-overrides"
|
||||
{{files :file
|
||||
files-2 "file"
|
||||
client :client
|
||||
client-2 "client"} :params
|
||||
user :identity}
|
||||
(let [files (or files files-2)
|
||||
client (or client client-2)
|
||||
{:keys [tempfile]} files]
|
||||
(assert-admin user)
|
||||
(try
|
||||
{:status 200
|
||||
:body (import-account-overrides client (.getPath tempfile))
|
||||
:headers {"Content-Type" "application/json"}}
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 500
|
||||
:body {:message (.getMessage e)
|
||||
:data (ex-data e)}
|
||||
:headers {"Content-Type" "application/json"}}))))))
|
||||
wrap-secure))
|
||||
:else
|
||||
:new))
|
||||
parsed-invoice-rows)
|
||||
vendors-not-found (->> parsed-invoice-rows
|
||||
(filter #(and (nil? (:vendor-id %))
|
||||
(not= "Cash" (:check %))))
|
||||
(map :vendor-name)
|
||||
set)
|
||||
_ (transact-with-ledger (invoice-rows->transaction (:new grouped-rows)
|
||||
user)
|
||||
user)]
|
||||
{:status 200
|
||||
:body (pr-str {:imported (count (:new grouped-rows))
|
||||
:already-imported (count (:exists grouped-rows))
|
||||
:vendors-not-found vendors-not-found
|
||||
:errors (map #(dissoc % :date) (:error grouped-rows))})
|
||||
:headers {"Content-Type" "application/edn"}}))
|
||||
|
||||
(defn cleared-against [{{files :file
|
||||
files-2 "file"} :params
|
||||
user :identity}]
|
||||
(let [files (or files files-2)
|
||||
{:keys [tempfile]} files]
|
||||
(assert-admin user)
|
||||
(try
|
||||
(import-transactions-cleared-against (.getPath tempfile))
|
||||
{:status 200
|
||||
:body (pr-str {})
|
||||
:headers {"Content-Type" "application/edn"}}
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 500
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)
|
||||
:data (ex-data e)})
|
||||
:headers {"Content-Type" "application/edn"}}))))
|
||||
|
||||
|
||||
|
||||
(defn bulk-account-overrides [{{files :file
|
||||
files-2 "file"
|
||||
client :client
|
||||
client-2 "client"} :params
|
||||
user :identity}]
|
||||
(let [files (or files files-2)
|
||||
client (or client client-2)
|
||||
{:keys [tempfile]} files]
|
||||
(assert-admin user)
|
||||
(try
|
||||
{:status 200
|
||||
:body (import-account-overrides client (.getPath tempfile))
|
||||
:headers {"Content-Type" "application/json"}}
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 500
|
||||
:body {:message (.getMessage e)
|
||||
:data (ex-data e)}
|
||||
:headers {"Content-Type" "application/json"}}))))
|
||||
|
||||
(def routes {"api/" {"transactions/" {:post {#"batch-upload/?" :batch-upload-transactions
|
||||
#"cleared-against/?" :cleared-against}}
|
||||
"invoices/" {:post {#"upload/?" :upload-invoices
|
||||
#"upload-integreat/?" :bulk-upload-invoices}}
|
||||
:post {#"account-overrides/?" :bulk-account-overrides}}})
|
||||
|
||||
(def match->handler {:batch-upload-transactions (wrap-secure batch-upload-transactions)
|
||||
:upload-invoices (wrap-secure upload-invoices)
|
||||
:bulk-upload-invoices (wrap-secure bulk-upload-invoices)
|
||||
:cleared-against (wrap-secure cleared-against)
|
||||
:bulk-account-overrides (wrap-secure (wrap-json-response bulk-account-overrides))})
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
(ns auto-ap.routes.queries
|
||||
(:require [amazonica.aws.s3 :as s3]
|
||||
[auto-ap.datomic :refer [conn]]
|
||||
[auto-ap.graphql.utils :refer [assert-admin]]
|
||||
[clojure.data.csv :as csv]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.string :as str]
|
||||
[clojure.tools.logging :as log]
|
||||
[com.unbounce.dogstatsd.core :as statsd]
|
||||
[compojure.core
|
||||
:refer
|
||||
[context defroutes GET POST PUT routes wrap-routes]]
|
||||
[config.core :refer [env]]
|
||||
[clojure.edn :as edn]
|
||||
[datomic.api :as d]
|
||||
[ring.middleware.json :refer [wrap-json-response]]
|
||||
[ring.util.request :refer [body-string]]
|
||||
[unilog.context :as lc])
|
||||
(:import java.util.UUID))
|
||||
(:require
|
||||
[amazonica.aws.s3 :as s3]
|
||||
[auto-ap.datomic :refer [conn]]
|
||||
[auto-ap.graphql.utils :refer [assert-admin]]
|
||||
[clojure.data.csv :as csv]
|
||||
[clojure.edn :as edn]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.string :as str]
|
||||
[clojure.tools.logging :as log]
|
||||
[com.unbounce.dogstatsd.core :as statsd]
|
||||
[config.core :refer [env]]
|
||||
[datomic.api :as d]
|
||||
[ring.middleware.json :refer [wrap-json-response]]
|
||||
[ring.util.request :refer [body-string]]
|
||||
[unilog.context :as lc])
|
||||
(:import
|
||||
(java.util UUID)))
|
||||
|
||||
(defn wrap-csv-response [handler]
|
||||
(fn [request]
|
||||
@@ -62,64 +61,77 @@
|
||||
:csv-results-url (str "/api/queries/" guid "/results/csv")
|
||||
:json-results-url (str "/api/queries/" guid "/results/json")}}))
|
||||
|
||||
(def json-routes
|
||||
(context "/queries" []
|
||||
(POST "/" {:keys [query-params identity] :as request}
|
||||
(assert-admin identity)
|
||||
(log/info "Note" (query-params "note"))
|
||||
(put-query (str (UUID/randomUUID)) (body-string request) (query-params "note")))
|
||||
(PUT "/:query-id" {:keys [query-params identity params] :as request}
|
||||
(assert-admin identity)
|
||||
(log/info "Note" (query-params "note"))
|
||||
(put-query (:query-id params) (body-string request) (query-params "note")))
|
||||
(GET "/:query-id" {:keys [identity params]}
|
||||
(assert-admin identity)
|
||||
(let [{:keys [query-id]} params
|
||||
obj (s3/get-object :bucket-name (:data-bucket env)
|
||||
:key (str "queries/" query-id))
|
||||
query-string (str (slurp (:object-content obj)))]
|
||||
(log/info obj)
|
||||
{:body {:query query-string
|
||||
:note (:note (:user-metadata (:object-metadata obj)))
|
||||
:id query-id
|
||||
:csv-results-url (str "/api/queries/" query-id "/results/csv")
|
||||
:json-results-url (str "/api/queries/" query-id "/results/json")}}))
|
||||
|
||||
|
||||
(GET "/" {:keys [identity]}
|
||||
(assert-admin identity)
|
||||
(let [obj (s3/list-objects :bucket-name (:data-bucket env)
|
||||
:prefix (str "queries/"))]
|
||||
(log/info obj)
|
||||
{:body (->> (:object-summaries obj)
|
||||
(map (fn [o]
|
||||
{:last-modified (.toString (:last-modified o))
|
||||
:key (str/replace (:key o) #"^queries\/" "")})))}))
|
||||
|
||||
(GET "/:query-id/results/json" {:keys [query-params params]}
|
||||
(statsd/time! [(str "export.query.time") {:tags #{(str "query:" (:query-id params))}}]
|
||||
{:body (execute-query query-params params)}))))
|
||||
|
||||
|
||||
(def raw-routes
|
||||
(context "/queries" []
|
||||
(GET "/:query-id/raw" {:keys [identity params]}
|
||||
(assert-admin identity)
|
||||
(let [{:keys [query-id]} params
|
||||
obj (s3/get-object :bucket-name (:data-bucket env)
|
||||
:key (str "queries/" query-id))
|
||||
query-string (str (slurp (:object-content obj)))]
|
||||
(log/info obj)
|
||||
{:body query-string}))))
|
||||
(defn get-queries [{:keys [identity]}]
|
||||
(assert-admin identity)
|
||||
(let [obj (s3/list-objects :bucket-name (:data-bucket env)
|
||||
:prefix (str "queries/"))]
|
||||
(log/info obj)
|
||||
{:body (->> (:object-summaries obj)
|
||||
(map (fn [o]
|
||||
{:last-modified (.toString (:last-modified o))
|
||||
:key (str/replace (:key o) #"^queries\/" "")})))}))
|
||||
|
||||
(defn create-query [{:keys [query-params identity] :as request}]
|
||||
(assert-admin identity)
|
||||
(log/info "Note" (query-params "note"))
|
||||
(put-query (str (UUID/randomUUID)) (body-string request) (query-params "note")))
|
||||
|
||||
(defn get-query [{:keys [identity params]} ]
|
||||
(assert-admin identity)
|
||||
(let [{:keys [query-id]} params
|
||||
obj (s3/get-object :bucket-name (:data-bucket env)
|
||||
:key (str "queries/" query-id))
|
||||
query-string (str (slurp (:object-content obj)))]
|
||||
(log/info obj)
|
||||
{:body {:query query-string
|
||||
:note (:note (:user-metadata (:object-metadata obj)))
|
||||
:id query-id
|
||||
:csv-results-url (str "/api/queries/" query-id "/results/csv")
|
||||
:json-results-url (str "/api/queries/" query-id "/results/json")}}))
|
||||
|
||||
(defn update-query [{:keys [query-params identity params] :as request} ]
|
||||
(assert-admin identity)
|
||||
(log/info "Note" (query-params "note"))
|
||||
(put-query (:query-id params) (body-string request) (query-params "note")))
|
||||
|
||||
(defn results-json-query [{:keys [query-params params]}]
|
||||
(statsd/time! [(str "export.query.time") {:tags #{(str "query:" (:query-id params))}}]
|
||||
{:body (execute-query query-params params)}))
|
||||
|
||||
(defn raw-query [{:keys [identity params]}]
|
||||
(assert-admin identity)
|
||||
(let [{:keys [query-id]} params
|
||||
obj (s3/get-object :bucket-name (:data-bucket env)
|
||||
:key (str "queries/" query-id))
|
||||
query-string (str (slurp (:object-content obj)))]
|
||||
(log/info obj)
|
||||
{:body query-string}))
|
||||
|
||||
(defn results-csv-query [{:keys [query-params params]}]
|
||||
(statsd/time! [(str "export.query.time") {:tags #{(str "query:" (:query-id params))}}]
|
||||
{:body (execute-query query-params params)}))
|
||||
|
||||
(def routes2 {"api/" {#"queries/?" {[:query-id "/raw"] {:get :raw-query}
|
||||
[:query-id "/results/csv"] {:get :results-csv-query}
|
||||
[:query-id "/results/json"] {:get :results-json-query}
|
||||
[:query-id] {:get :get-query
|
||||
:put :update-query}
|
||||
:get :get-queries
|
||||
:post :create-query}}})
|
||||
|
||||
(def match->handler {:get-queries get-queries
|
||||
:create-query create-query
|
||||
:raw-query raw-query
|
||||
:get-query (-> get-query
|
||||
wrap-json-response)
|
||||
:update-query (-> update-query
|
||||
wrap-json-response)
|
||||
|
||||
:results-json-query (-> results-json-query
|
||||
wrap-json-response)
|
||||
:results-csv-query (-> results-csv-query
|
||||
wrap-csv-response)})
|
||||
|
||||
|
||||
(def csv-routes
|
||||
(context "/queries" []
|
||||
(GET "/:query-id/results/csv" {:keys [query-params params]}
|
||||
(statsd/time! [(str "export.query.time") {:tags #{(str "query:" (:query-id params))}}]
|
||||
{:body (execute-query query-params params)}))))
|
||||
(defroutes query2-routes
|
||||
(routes
|
||||
raw-routes
|
||||
(wrap-routes json-routes
|
||||
wrap-json-response)
|
||||
(wrap-routes csv-routes wrap-csv-response)))
|
||||
|
||||
@@ -1,9 +1,29 @@
|
||||
(ns auto-ap.routes.utils
|
||||
(:require [buddy.auth :refer [authenticated?]]))
|
||||
(:require
|
||||
[auto-ap.graphql.utils :refer [is-admin?]]
|
||||
[buddy.auth :refer [authenticated?]]
|
||||
[auto-ap.logging :as alog]
|
||||
[cemerick.url :as url]))
|
||||
|
||||
(defn wrap-secure [handler]
|
||||
(fn [request]
|
||||
(if (authenticated? request)
|
||||
(handler request)
|
||||
{:status 401
|
||||
:body "not authenticated"})))
|
||||
{:status 302
|
||||
:headers {"Location" "/login" }})))
|
||||
|
||||
(defn wrap-admin [handler]
|
||||
(fn [request]
|
||||
(if (is-admin? (:identity request))
|
||||
(handler request)
|
||||
(do
|
||||
(alog/warn ::unauthenticated)
|
||||
{:status 302
|
||||
:headers {"Location" "/login"}}))))
|
||||
|
||||
(defn wrap-client-redirect-unauthenticated [handler]
|
||||
(fn [request]
|
||||
(let [response (handler request)]
|
||||
(if (= 401 (get response :status))
|
||||
(assoc-in response [:headers "hx-redirect"] "/login/")
|
||||
response))))
|
||||
|
||||
@@ -6,99 +6,113 @@
|
||||
[auto-ap.routes.utils :refer [wrap-secure]]
|
||||
[auto-ap.yodlee.core2 :as yodlee]
|
||||
[clojure.tools.logging :as log]
|
||||
[compojure.core :refer [context defroutes GET POST wrap-routes]]
|
||||
[config.core :refer [env]]
|
||||
[datomic.api :as d]))
|
||||
|
||||
(defroutes routes
|
||||
(wrap-routes
|
||||
(context "/yodlee2" []
|
||||
(GET "/fastlink" {:keys [query-params identity]}
|
||||
(assert-can-see-client identity (d/pull (d/db conn) [:db/id] [:client/code (get query-params "client")]))
|
||||
|
||||
(let [token (if-let [client-id (get query-params "client-id")]
|
||||
(-> client-id
|
||||
Long/parseLong
|
||||
d-clients/get-by-id
|
||||
:client/code
|
||||
(yodlee/get-access-token))
|
||||
(yodlee/get-access-token (get query-params "client")))]
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str {:token token
|
||||
:url (:yodlee2-fastlink env)}) }))
|
||||
(POST "/provider-accounts/refresh/" {:keys [identity edn-params]}
|
||||
(assert-admin identity)
|
||||
(log/info "refreshing " edn-params)
|
||||
(try
|
||||
(yodlee/refresh-provider-account (-> (:client-id edn-params)
|
||||
Long/parseLong
|
||||
d-clients/get-by-id
|
||||
:client/code)
|
||||
(:provider-account-id edn-params))
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body "{}" }
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 400
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)})})))
|
||||
(defn fastlink [{:keys [query-params identity]}]
|
||||
(assert-can-see-client identity (d/pull (d/db conn) [:db/id] [:client/code (get query-params "client")]))
|
||||
|
||||
(let [token (if-let [client-id (get query-params "client-id")]
|
||||
(-> client-id
|
||||
Long/parseLong
|
||||
d-clients/get-by-id
|
||||
:client/code
|
||||
(yodlee/get-access-token))
|
||||
(yodlee/get-access-token (get query-params "client")))]
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str {:token token
|
||||
:url (:yodlee2-fastlink env)}) }))
|
||||
(defn refresh-provider-accounts [{:keys [identity edn-params]}]
|
||||
(assert-admin identity)
|
||||
(log/info "refreshing " edn-params)
|
||||
(try
|
||||
(yodlee/refresh-provider-account (-> (:client-id edn-params)
|
||||
Long/parseLong
|
||||
d-clients/get-by-id
|
||||
:client/code)
|
||||
(:provider-account-id edn-params))
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body "{}" }
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 400
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)})})))
|
||||
|
||||
(GET "/provider-accounts/:client/:id" {:keys [identity]
|
||||
{:keys [client id]} :route-params}
|
||||
(assert-admin identity)
|
||||
(log/info "looking-up " client id)
|
||||
(try
|
||||
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str (yodlee/get-provider-account-detail (-> client
|
||||
Long/parseLong
|
||||
d-clients/get-by-id
|
||||
:client/code)
|
||||
id))}
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 400
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)})})))
|
||||
(POST "/provider-accounts/delete/" {:keys [edn-params identity]}
|
||||
(assert-admin identity)
|
||||
(try
|
||||
(yodlee/delete-provider-account (-> (:client-id edn-params)
|
||||
Long/parseLong
|
||||
d-clients/get-by-id
|
||||
:client/code)
|
||||
(:provider-account-id edn-params))
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str {}) }
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 400
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)})})))
|
||||
(POST "/reauthenticate/:id" {:keys [identity] {:keys [id]} :route-params
|
||||
data :edn-params}
|
||||
(assert-admin identity)
|
||||
(try
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str (yodlee/reauthenticate-and-recache
|
||||
(-> (:client-id data)
|
||||
Long/parseLong
|
||||
d-clients/get-by-id
|
||||
:client/code)
|
||||
(Long/parseLong id)
|
||||
(dissoc data :client-id )))}
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 500
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)})}))))
|
||||
wrap-secure))
|
||||
(defn get-provider-account-detail [{:keys [identity]
|
||||
{:keys [client id]} :route-params}]
|
||||
(assert-admin identity)
|
||||
(log/info "looking-up " client id)
|
||||
(try
|
||||
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str (yodlee/get-provider-account-detail (-> client
|
||||
Long/parseLong
|
||||
d-clients/get-by-id
|
||||
:client/code)
|
||||
id))}
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 400
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)})})))
|
||||
(defn reauthenticate [{:keys [identity] {:keys [id]} :route-params
|
||||
data :edn-params}]
|
||||
(assert-admin identity)
|
||||
(try
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str (yodlee/reauthenticate-and-recache
|
||||
(-> (:client-id data)
|
||||
Long/parseLong
|
||||
d-clients/get-by-id
|
||||
:client/code)
|
||||
(Long/parseLong id)
|
||||
(dissoc data :client-id )))}
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 500
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)})})))
|
||||
|
||||
(defn delete-provider-account [{:keys [edn-params identity]}]
|
||||
(assert-admin identity)
|
||||
(try
|
||||
(yodlee/delete-provider-account (-> (:client-id edn-params)
|
||||
Long/parseLong
|
||||
d-clients/get-by-id
|
||||
:client/code)
|
||||
(:provider-account-id edn-params))
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str {}) }
|
||||
(catch Exception e
|
||||
(log/error e)
|
||||
{:status 400
|
||||
:headers {"Content-Type" "application/edn"}
|
||||
:body (pr-str {:message (.getMessage e)
|
||||
:error (.toString e)})})))
|
||||
|
||||
(defn valid-for [handler & methods]
|
||||
(let [methods (into #{} methods)]
|
||||
(fn [request]
|
||||
(if (methods (:request-method request))
|
||||
(handler request)
|
||||
{:status 404}))))
|
||||
|
||||
(def routes {"api" {"/yodlee2" {"/fastlink" :fastlink
|
||||
"/provider-accounts/refresh/" :refresh-provider-accounts
|
||||
["/provider-accounts/" :client "/" :id ] :get-provider-account-detail
|
||||
["/reauthenticate/" :id ] :reauthenticate
|
||||
"/provider-accounts/delete/" :delete-provider-account}}})
|
||||
(def match->handler {:fastlink (-> fastlink wrap-secure (valid-for :get))
|
||||
:refresh-provider-accounts (-> refresh-provider-accounts wrap-secure (valid-for :post))
|
||||
:get-provider-account-detail (-> get-provider-account-detail wrap-secure (valid-for :get))
|
||||
:reauthenticate (-> reauthenticate wrap-secure (valid-for :post))
|
||||
:delete-provider-account (-> delete-provider-account wrap-secure (valid-for :post))} )
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
(.setHandler server stats-handler))
|
||||
(.setStopAtShutdown server true))
|
||||
|
||||
(mount/defstate port :start (Integer/parseInt (or (env :port) "3000")))
|
||||
(mount/defstate port :start (Integer/parseInt (or "3001" (env :port) "3000")))
|
||||
(mount/defstate jetty
|
||||
:start (run-jetty app {:port port
|
||||
:join? false
|
||||
|
||||
@@ -47,11 +47,16 @@
|
||||
:attempt attempt
|
||||
:source "Square 3"
|
||||
:background-job "Square 3")
|
||||
(client/request (assoc request
|
||||
:socket-timeout 10000
|
||||
:connection-timeout 10000
|
||||
:as :json
|
||||
:retry-handler retry-4))))
|
||||
(try
|
||||
(client/request (assoc request
|
||||
:socket-timeout 10000
|
||||
:connection-timeout 10000
|
||||
:as :json
|
||||
:retry-handler retry-4))
|
||||
(catch Exception e
|
||||
(log/error ::raw-request-failed
|
||||
:exception e)
|
||||
(throw e)))))
|
||||
(de/catch
|
||||
(fn [e]
|
||||
(if (= attempt 5)
|
||||
|
||||
@@ -1,94 +1,16 @@
|
||||
(ns auto-ap.ssr.admin
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn]]
|
||||
[auto-ap.graphql.utils :refer [assert-admin]]
|
||||
[auto-ap.logging :as alog]
|
||||
[auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.ssr.ui :refer [base-page html-response]]
|
||||
[auto-ap.time :as atime]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clojure.string :as str]
|
||||
[clojure.tools.logging :as log]
|
||||
[compojure.core :refer [context defroutes GET POST routes]]
|
||||
[datomic.api :as d]
|
||||
[hiccup2.core :as hiccup]))
|
||||
|
||||
(defn html-page [hiccup]
|
||||
{:status 200
|
||||
:headers {"Content-Type" "text/html"}
|
||||
:body (str
|
||||
"<!DOCTYPE html>"
|
||||
(hiccup/html
|
||||
{}
|
||||
hiccup))})
|
||||
|
||||
(defn base-page [contents]
|
||||
(html-page
|
||||
[:html.has-navbar-fixed-top
|
||||
[:head
|
||||
[:meta {:charset "utf-8"}]
|
||||
[:meta {:http-equiv "X-UA-Compatible", :content "IE=edge"}]
|
||||
[:meta {:name "viewport", :content "width=device-width, initial-scale=1"}]
|
||||
[:title "Integreat"]
|
||||
[:link {:rel "stylesheet", :href "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css", :integrity "sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=", :crossorigin "anonymous"}]
|
||||
[:link {:href "/css/font.min.css", :rel "stylesheet"}]
|
||||
[:link {:rel "stylesheet", :href "/css/bulma.min.css"}]
|
||||
[:link {:rel "stylesheet", :href "/css/bulma-calendar.min.css"}]
|
||||
[:link {:rel "stylesheet", :href "/css/bulma-badge.min.css"}]
|
||||
[:link {:rel "stylesheet", :href "/css/react-datepicker.min.inc.css"}]
|
||||
[:link {:rel "stylesheet", :href "/css/animate.css"}]
|
||||
[:link {:rel "stylesheet", :href "/finance-font/style.css"}]
|
||||
[:link {:rel "stylesheet", :href "/css/main.css"}]
|
||||
[:link {:rel "stylesheet", :href "https://unpkg.com/placeholder-loading/dist/css/placeholder-loading.min.css"}]
|
||||
[:script {:src "https://unpkg.com/hyperscript.org@0.9.7"}]
|
||||
[:script {:src "https://unpkg.com/htmx.org@1.8.4"
|
||||
:integrity "sha384-wg5Y/JwF7VxGk4zLsJEcAojRtlVp1FKKdGy1qN+OMtdq72WRvX/EdRdqg/LOhYeV"
|
||||
:crossorigin= "anonymous"}]
|
||||
[:script {:type "text/javascript", :src "https://cdn.yodlee.com/fastlink/v4/initialize.js", :async "async" }]]
|
||||
|
||||
|
||||
[:body
|
||||
[:div {:id "app"}
|
||||
[:div
|
||||
[:nav {:class "navbar has-shadow is-fixed-top is-grey"}
|
||||
|
||||
[:div {:class "container"}
|
||||
[:div {:class "navbar-brand"}
|
||||
[:a {:class "navbar-item", :href "../"}
|
||||
[:img {:src "/img/logo.png"}]]]
|
||||
[:div.navbar-menu {:id "navMenu"}
|
||||
[:div.navbar-start
|
||||
[:a.navbar-item {:href "/"}
|
||||
"Home" ]
|
||||
[:a.navbar-item {:href "/invoices/"}
|
||||
"Invoices" ]
|
||||
[:a.navbar-item {:href "/payments/"}
|
||||
"Payments" ]
|
||||
[:a.navbar-item {:href "/pos/sales-orders/"}
|
||||
"POS" ]
|
||||
[:a.navbar-item {:href "/transactions/"}
|
||||
"Transactions" ]
|
||||
|
||||
[:a.navbar-item {:href "/ledger/"}
|
||||
"Ledger" ]]]]]
|
||||
[:div {:class "columns has-shadow", :id "mail-app", :style "margin-bottom: 0px; height: calc(100vh - 46px);"}
|
||||
[:aside {:class "column aside menu is-2 "}
|
||||
[:div {:class "main left-nav"}
|
||||
[:div]]]
|
||||
[:div {:class "column messages hero ", :id "message-feed", :style "overflow: auto;"}
|
||||
[:div {:class "inbox-messages"}
|
||||
contents]]]
|
||||
[:div]
|
||||
[:div {:id "dz-hidden"}]]]]]))
|
||||
|
||||
(defn html-response [hiccup]
|
||||
{:status 200
|
||||
:headers {"Content-Type" "text/html"}
|
||||
:body (str
|
||||
(hiccup/html
|
||||
{}
|
||||
hiccup))})
|
||||
|
||||
|
||||
(defn inline-add-deletes [history]
|
||||
(defn tx-rows->changes [history]
|
||||
(->> history
|
||||
(group-by (fn [[a _ t]]
|
||||
[a t]))
|
||||
@@ -102,6 +24,9 @@
|
||||
changes))]
|
||||
[t a changes])))))
|
||||
|
||||
(def error-script
|
||||
(hiccup/raw "on htmx:responseError from me set event.detail.target's innerHTML to event.detail.xhr.responseText end"))
|
||||
|
||||
(defn format-value [v]
|
||||
(cond (inst? v)
|
||||
(-> v
|
||||
@@ -121,29 +46,27 @@
|
||||
:hx-target "#history-table"}
|
||||
v]
|
||||
" [" [:a
|
||||
{:hx-get (str "/admin/history/inspect/" v)
|
||||
:hx-swap "innerHTML"
|
||||
:hx-target "#inspector"
|
||||
:hx-trigger "click"}
|
||||
"snapshot"] "]"
|
||||
]
|
||||
|
||||
{:hx-get (str "/admin/history/inspect/" v)
|
||||
:hx-swap "innerHTML"
|
||||
:hx-target "#inspector"
|
||||
:hx-trigger "click"
|
||||
"_" error-script}
|
||||
"snapshot"] "]"]
|
||||
|
||||
|
||||
:else
|
||||
(pr-str v)))
|
||||
|
||||
(comment "_" )
|
||||
|
||||
(defn page-template [& {:keys [table entity-id]}]
|
||||
[:div
|
||||
[:div.columns
|
||||
[:div.column.is-4
|
||||
[:form.hello {"hx-target" "#history-table"
|
||||
"hx-post" "/admin/history/search"
|
||||
"hx-swap" "innerHTML"
|
||||
"_" (hiccup/raw "on htmx:beforeRequest toggle @disabled on me then toggle .is-loading on <#dig/> end
|
||||
[:form {"hx-target" "#history-table"
|
||||
"hx-post" "/admin/history/search"
|
||||
"hx-swap" "innerHTML"
|
||||
"_" (hiccup/raw "on htmx:beforeRequest toggle @disabled on me then toggle .is-loading on <#dig/> end
|
||||
on htmx:afterRequest toggle @disabled on me then toggle .is-loading on <#dig /> end")
|
||||
}
|
||||
}
|
||||
[:div.field.is-grouped
|
||||
[:p.control {}
|
||||
[:input.input {:type "text" :name "entity-id" :placeholder "Entity id" :value entity-id}]]
|
||||
@@ -153,27 +76,63 @@
|
||||
[:div#history-table
|
||||
table]])
|
||||
|
||||
(defn history-search [{:keys [form-params params] identity :identity :as request}]
|
||||
(assert-admin identity)
|
||||
(log/info ::request
|
||||
request)
|
||||
(defn table [entity-id best-guess-entity history]
|
||||
[:div [:h1.title "History for "
|
||||
(str/capitalize best-guess-entity)
|
||||
" "
|
||||
entity-id]
|
||||
[:div.columns
|
||||
[:div.column.is-9
|
||||
[:table.table.compact.grid {:style "width: 100%"}
|
||||
[:thead
|
||||
[:tr
|
||||
[:td {:style "width: 14em"} "Date"]
|
||||
[:td {:style "width: 14em"} "User"]
|
||||
[:td {:style "width: 18em"} "Field"]
|
||||
[:td "From"]
|
||||
[:td "To"]]]
|
||||
[:tbody
|
||||
(for [[tx a c] history]
|
||||
[:tr
|
||||
[:td [:div [:div (some-> (:db/txInstant tx)
|
||||
coerce/to-date-time
|
||||
atime/localize
|
||||
(atime/unparse atime/standard-time))
|
||||
]
|
||||
[:div.tag (:db/id tx)]]]
|
||||
[:td (str (:audit/user tx))]
|
||||
[:td (namespace a) ": " (name a)]
|
||||
|
||||
[:td
|
||||
[:div.tag.is-danger.is-light
|
||||
[:span
|
||||
(format-value (:removed c))]]]
|
||||
[:td
|
||||
[:div.tag.is-primary.is-light
|
||||
[:span
|
||||
(format-value (:added c))]]]])]
|
||||
]]
|
||||
[:div.column.is-3
|
||||
[:div#inspector]]]])
|
||||
|
||||
(defn history-search [{:keys [form-params params] :as request}]
|
||||
(try
|
||||
(let [entity-id (Long/parseLong (or (some-> (:entity-id form-params) not-empty)
|
||||
(let [entity-id (Long/parseLong (or (some-> (:entity-id form-params) not-empty)
|
||||
(:entity-id params)
|
||||
(get params "entity-id")
|
||||
(get form-params "entity-id")))
|
||||
history (->>
|
||||
(d/q '[:find ?a2 ?v (pull ?tx [:db/txInstant :audit/user :db/id]) ?ad
|
||||
:in $ $$ ?i
|
||||
:where
|
||||
[$$ ?i ?a ?v ?tx ?ad]
|
||||
[$ ?a :db/ident ?a2]]
|
||||
(d/db conn)
|
||||
(d/history (d/db conn))
|
||||
entity-id )
|
||||
inline-add-deletes
|
||||
(sort-by (comp :db/id first))
|
||||
vec)
|
||||
history (->>
|
||||
(d/q '[:find ?a2 ?v (pull ?tx [:db/txInstant :audit/user :db/id]) ?ad
|
||||
:in $ $$ ?i
|
||||
:where
|
||||
[$$ ?i ?a ?v ?tx ?ad]
|
||||
[$ ?a :db/ident ?a2]]
|
||||
(d/db conn)
|
||||
(d/history (d/db conn))
|
||||
entity-id )
|
||||
tx-rows->changes
|
||||
(sort-by (comp :db/id first))
|
||||
vec)
|
||||
best-guess-entity (or (->> history
|
||||
(group-by
|
||||
(comp
|
||||
@@ -185,99 +144,55 @@
|
||||
(sort-by second)
|
||||
last
|
||||
first)
|
||||
"?")
|
||||
table [:div [:h1.title "History for "
|
||||
(str/capitalize best-guess-entity)
|
||||
" "
|
||||
entity-id]
|
||||
[:div.columns
|
||||
[:div.column.is-9
|
||||
[:table.table.compact.grid {:style "width: 100%"}
|
||||
[:thead
|
||||
[:tr
|
||||
[:td {:style "width: 14em"} "Date"]
|
||||
[:td {:style "width: 14em"} "User"]
|
||||
[:td {:style "width: 18em"} "Field"]
|
||||
[:td "From"]
|
||||
[:td "To"]]]
|
||||
[:tbody
|
||||
(for [[tx a c] history]
|
||||
[:tr
|
||||
[:td [:div [:div (some-> (:db/txInstant tx)
|
||||
coerce/to-date-time
|
||||
atime/localize
|
||||
(atime/unparse atime/standard-time))
|
||||
]
|
||||
[:div.tag (:db/id tx)]]]
|
||||
[:td (str (:audit/user tx))]
|
||||
[:td (namespace a) ": " (name a)]
|
||||
"?")]
|
||||
|
||||
[:td
|
||||
[:div.tag.is-danger.is-light
|
||||
[:span
|
||||
(format-value (:removed c))]]]
|
||||
[:td
|
||||
[:div.tag.is-primary.is-light
|
||||
[:span
|
||||
(format-value (:added c))]]]])]
|
||||
]]
|
||||
[:div.column.is-3
|
||||
[:div#inspector.box {:style {:position "sticky"
|
||||
:display "inline-block"
|
||||
:vertical-align "top"
|
||||
:overflow-y "auto"
|
||||
:max-height "100vh"
|
||||
:top "0px"
|
||||
:bottom "0px"}}]]]]]
|
||||
|
||||
(alog/info ::trace
|
||||
:bge best-guess-entity
|
||||
:headers (:headers request))
|
||||
(if (get (:headers request) "hx-request")
|
||||
(html-response
|
||||
table)
|
||||
(base-page (page-template :table table
|
||||
:entity-id entity-id))))
|
||||
(catch NumberFormatException e
|
||||
(table entity-id best-guess-entity history))
|
||||
(base-page (page-template :table (table entity-id best-guess-entity history)
|
||||
:entity-id entity-id)
|
||||
(admin-side-bar :admin-history))))
|
||||
(catch NumberFormatException _
|
||||
(html-response
|
||||
(str [:div.notification.is-danger.is-light
|
||||
"Cannot parse the entity-id " (or (:entity-id form-params)
|
||||
(:entity-id params))
|
||||
[:div.notification.is-danger.is-light
|
||||
"Cannot parse the entity-id " (or (:entity-id form-params)
|
||||
(:entity-id params))
|
||||
|
||||
". It should be a number."])))))
|
||||
". It should be a number."]))))
|
||||
|
||||
(defn inspect [{{:keys [entity-id]} :params identity :identity :as request}]
|
||||
(defn inspect [{{:keys [entity-id]} :params :as request}]
|
||||
(alog/info ::inspect
|
||||
:request request)
|
||||
(assert-admin identity)
|
||||
(try
|
||||
(let [entity-id (Long/parseLong entity-id)
|
||||
data (d/pull (d/db conn)
|
||||
'[*]
|
||||
entity-id
|
||||
) ]
|
||||
entity-id)]
|
||||
|
||||
(html-response
|
||||
[:div {:style {:display "inline-block"}}
|
||||
[:h1.title "Snapshot of "
|
||||
entity-id]
|
||||
[:ul
|
||||
(for [[k v] data]
|
||||
[:li [:strong k] ":" v]
|
||||
)]]))
|
||||
(catch NumberFormatException e
|
||||
[:div.box {:style {:position "sticky"
|
||||
:display "inline-block"
|
||||
:vertical-align "top"
|
||||
:overflow-y "auto"
|
||||
:max-height "100vh"
|
||||
:top "0px"
|
||||
:bottom "0px"}}
|
||||
[:div {:style {:display "inline-block"}}
|
||||
[:h1.title "Snapshot of "
|
||||
entity-id]
|
||||
[:ul
|
||||
(for [[k v] data]
|
||||
[:li [:strong k] ":" (format-value v)]
|
||||
)]]]))
|
||||
(catch NumberFormatException _
|
||||
(html-response
|
||||
[:div.notification.is-danger.is-light
|
||||
"Cannot parse the entity-id " entity-id ". It should be a number."]))))
|
||||
|
||||
(defn history [{:keys [identity] :as request}]
|
||||
(base-page (page-template )))
|
||||
(defn history [{:keys [matched-route]}]
|
||||
(base-page (page-template )
|
||||
(admin-side-bar matched-route)))
|
||||
|
||||
|
||||
|
||||
|
||||
(defroutes admin-routes
|
||||
(routes
|
||||
(context "/admin" []
|
||||
(GET "/history" [] history)
|
||||
(GET "/history/" [] history)
|
||||
(POST "/history/search" [] history-search)
|
||||
(GET "/history/:entity-id" [entity-id] history-search)
|
||||
(GET "/history/inspect/:entity-id" [entity-id] inspect))))
|
||||
|
||||
264
src/clj/auto_ap/ssr/company/company_1099.clj
Normal file
264
src/clj/auto_ap/ssr/company/company_1099.clj
Normal file
@@ -0,0 +1,264 @@
|
||||
(ns auto-ap.ssr.company.company-1099
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn remove-nils]]
|
||||
[auto-ap.graphql.utils :refer [can-see-client?]]
|
||||
[auto-ap.shared-views.company.sidebar :refer [company-side-bar]]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[auto-ap.ssr.ui :refer [base-page html-response]]
|
||||
[bidi.bidi :as bidi]
|
||||
[clojure.string :as str]
|
||||
[datomic.api :as d]
|
||||
[hiccup2.core :as hiccup]))
|
||||
|
||||
(defn cannot-overwrite? [vendor]
|
||||
(some? (or (:vendor/legal-entity-1099-type vendor)
|
||||
(:vendor/legal-entity-tin vendor)
|
||||
(:vendor/legal-entity-tin-type vendor))))
|
||||
|
||||
(defn get-1099-companies [user]
|
||||
(->> (d/q '[:find
|
||||
(pull ?c [:client/code])
|
||||
(pull ?v [:db/id
|
||||
:vendor/name
|
||||
{:vendor/legal-entity-1099-type [:db/ident]}
|
||||
{:vendor/legal-entity-tin-type [:db/ident]}
|
||||
{:vendor/address [:address/street1
|
||||
:address/city
|
||||
:address/state
|
||||
:address/zip]}
|
||||
:vendor/legal-entity-first-ein
|
||||
:vendor/legal-entity-first-name
|
||||
:vendor/legal-entity-middle-name
|
||||
:vendor/legal-entity-last-name])
|
||||
(sum ?a)
|
||||
:with ?p
|
||||
:in $ ?user
|
||||
:where [?p :payment/date ?d ]
|
||||
[(>= ?d #inst "2018-01-01T08:00")]
|
||||
[(< ?d #inst "2023-01-01T08:00")]
|
||||
[?p :payment/client ?c]
|
||||
[(auto-ap.graphql.utils/can-see-client? ?user ?c)]
|
||||
[?p :payment/amount ?a]
|
||||
[?p :payment/type :payment-type/check]
|
||||
[?p :payment/vendor ?v]]
|
||||
(d/db conn)
|
||||
user)
|
||||
(filter (fn [[_ _ a]]
|
||||
(>= a 600.0)))
|
||||
|
||||
(sort-by (fn [[client _ amount]]
|
||||
[(:client/code client) amount]) )))
|
||||
|
||||
(defn dialog [header content footer]
|
||||
[:div.modal.is-active
|
||||
[:div.modal-background {"_" (hiccup/raw "on click remove <#modal-holder div/>")}]
|
||||
[:div.modal-card
|
||||
[:div.modal-card-head
|
||||
header]
|
||||
[:div.modal-card-body
|
||||
content]
|
||||
[:div.modal-card-foot
|
||||
footer]]
|
||||
[:button.modal-close.is-large {"_" (hiccup/raw "on click remove <#modal-holder div/>")}]])
|
||||
|
||||
(defn table [{:keys [identity]} & {:keys [flash-id]}]
|
||||
[:table#vendor-table.table.grid.compact.is-fullwidth
|
||||
[:thead
|
||||
[:tr
|
||||
[:th {:style {:width "5em"}}"Client"]
|
||||
[:th "Vendor Name"]
|
||||
[:th "Name"]
|
||||
[:th {:style {:width "9em"}} "1099 Type"]
|
||||
[:th {:style {:width "8em"}} "TIN"]
|
||||
[:th "Address"]
|
||||
[:th "Amount Paid"]
|
||||
[:th {:style {:width "10em"}}]
|
||||
]]
|
||||
[:tbody
|
||||
(for [[client vendor amount] (get-1099-companies identity)]
|
||||
[:tr (when (= flash-id
|
||||
(:db/id vendor))
|
||||
{:class "live-added"})
|
||||
[:td (:client/code client)]
|
||||
[:td (:vendor/name vendor)]
|
||||
[:td (-> vendor :vendor/legal-entity-first-name) " "
|
||||
(-> vendor :vendor/legal-entity-middle-name) " "
|
||||
(-> vendor :vendor/legal-entity-last-name)]
|
||||
[:td (some-> vendor :vendor/legal-entity-1099-type :db/ident name)] " "
|
||||
[:td
|
||||
(some-> vendor :vendor/legal-entity-tin-type :db/ident name)
|
||||
(-> vendor :vendor/legal-entity-tin)]
|
||||
[:td
|
||||
(-> vendor :vendor/address :address/street1) " "
|
||||
(-> vendor :vendor/address :address/street2) " "
|
||||
(-> vendor :vendor/address :address/city) " "
|
||||
(-> vendor :vendor/address :address/state) " "
|
||||
(-> vendor :vendor/address :address/zip)
|
||||
[:td amount]
|
||||
[:td
|
||||
(if (cannot-overwrite? vendor)
|
||||
[:a {:href "mailto:ben@integreatconsult.com"} "Contact Integreat"]
|
||||
[:button.button {:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099-vendor-dialog
|
||||
:vendor-id (:db/id vendor))
|
||||
:hx-target "#modal-holder"
|
||||
:hx-swap "innerHTML"}
|
||||
[:span.icon [:i.fa.fa-pencil ]]])]]])]])
|
||||
|
||||
(defn form-data->map [form-data]
|
||||
(reduce-kv
|
||||
(fn [acc k v]
|
||||
(assoc-in acc (->> (str/split k #"_")
|
||||
(mapv #(apply keyword (str/split % #"/")))) v))
|
||||
{}
|
||||
form-data))
|
||||
|
||||
(defn path->name [k]
|
||||
(cond (keyword? k)
|
||||
(str (namespace k) "/" (name k))
|
||||
|
||||
(seq k)
|
||||
(str/join "_" (map path->name k))
|
||||
:else k))
|
||||
|
||||
(defn vendor-save [{:keys [form-params identity route-params] :as request}]
|
||||
(when-not (cannot-overwrite? (d/pull (d/db conn) '[*] (Long/parseLong (:vendor-id route-params))))
|
||||
@(d/transact conn [(remove-nils
|
||||
(-> (form-data->map form-params)
|
||||
(assoc :db/id (Long/parseLong (:vendor-id route-params)))
|
||||
(update :vendor/legal-entity-1099-type #(some->> % (keyword "legal-entity-1099-type")))
|
||||
(update :vendor/legal-entity-tin-type #(some->> % (keyword "legal-entity-tin-type")))))]))
|
||||
(html-response
|
||||
(table request :flash-id (Long/parseLong (:vendor-id route-params)))))
|
||||
|
||||
|
||||
(defn vendor-dialog [request]
|
||||
(let [vendor (d/pull (d/db conn) '[* {:vendor/legal-entity-1099-type [:db/ident]
|
||||
:vendor/legal-entity-tin-type [:db/ident]}] (Long/parseLong (:vendor-id (:params request))))] ;; TODO perms
|
||||
(html-response
|
||||
[:form {:hx-post (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099-vendor-save
|
||||
:request-method :post
|
||||
:vendor-id (Long/parseLong (:vendor-id (:params request))))
|
||||
:hx-target "#vendor-table"
|
||||
:hx-swap "outerHTML swap:0.2s"
|
||||
"_" (hiccup/raw "on htmx:afterRequest transition <#modal-holder .modal-background, #modal-holder .modal-card />'s opacity from 1.0 to 0 over 100ms then remove <#modal-holder */> ")}
|
||||
(dialog
|
||||
[:h4.is-4.title "Vendor 1099 Info"]
|
||||
[:div
|
||||
[:h3.is-3.title (:vendor/name vendor)]
|
||||
|
||||
[:h4.is-4.title "Address"]
|
||||
[:hr]
|
||||
[:div.field
|
||||
[:p.help "Street1"]
|
||||
[:div.control
|
||||
[:input.input.is-expanded {:type "text"
|
||||
:autofocus true
|
||||
:name (path->name [:vendor/address :address/street1])
|
||||
:placeholder "1700 Pennsylvania Ave"
|
||||
:value (-> vendor :vendor/address :address/street1)}]]]
|
||||
[:div.field
|
||||
[:p.help "Street 2"]
|
||||
[:div.control
|
||||
[:input.input.is-expanded {:type "text"
|
||||
:name (path->name [:vendor/address :address/street2])
|
||||
:placeholder "SUite 400"
|
||||
:value (-> vendor :vendor/address :address/street2)}]]]
|
||||
[:div.level
|
||||
[:div.level-left
|
||||
[:div.level-item
|
||||
[:div.field
|
||||
[:p.help "City"]
|
||||
[:div.control
|
||||
[:input.input.is-expanded {:type "text"
|
||||
:placeholder "Cupertino"
|
||||
:name (path->name [:vendor/address :address/city])
|
||||
:value (-> vendor :vendor/address :address/city)}]]]]
|
||||
[:div.level-item
|
||||
[:div.field
|
||||
[:p.help "State"]
|
||||
[:div.control
|
||||
[:input.input.is-expanded {:type "text"
|
||||
:style {:width "3em"}
|
||||
:placeholder "CA"
|
||||
:name (path->name [:vendor/address :address/state])
|
||||
:value (-> vendor :vendor/address :address/state)}]]]]
|
||||
[:div.level-item
|
||||
[:div.field
|
||||
[:p.help "Zip"]
|
||||
[:div.control
|
||||
[:input.input.is-expanded {:type "text"
|
||||
:placeholder "95014"
|
||||
:name (path->name [:vendor/address :address/zip])
|
||||
:value (-> vendor :vendor/address :address/zip)}]]]]]]
|
||||
[:h4.is-4.title "Legal Entity"]
|
||||
[:hr]
|
||||
[:div.level
|
||||
[:div.level-left
|
||||
[:div.level-item
|
||||
[:div.field
|
||||
[:p.help "First Name"]
|
||||
[:div.control
|
||||
[:input.input.is-expanded {:type "text"
|
||||
:placeholder "Josh"
|
||||
:name (path->name [:vendor/legal-entity-first-name])
|
||||
:value (-> vendor :vendor/legal-entity-first-name)}]]]]
|
||||
[:div.level-item
|
||||
[:div.field
|
||||
[:p.help "Middle Name"]
|
||||
[:div.control
|
||||
[:input.input.is-expanded {:type "text"
|
||||
:placeholder "Caleb"
|
||||
:name (path->name [:vendor/legal-entity-middle-name])
|
||||
:value (-> vendor :vendor/legal-entity-middle-name)}]]]]
|
||||
[:div.level-item
|
||||
[:div.field
|
||||
[:p.help "Last Name"]
|
||||
[:div.control
|
||||
[:input.input.is-expanded {:type "text"
|
||||
:placeholder "Smith"
|
||||
:name (path->name [:vendor/legal-entity-last-name])
|
||||
:value (-> vendor :vendor/legal-entity-last-name)}]]]]]]
|
||||
|
||||
[:div.level
|
||||
[:div.level-left
|
||||
[:div.level-item
|
||||
[:div.field
|
||||
[:p.help "TIN"]
|
||||
[:div.control
|
||||
[:input.input {:type "text"
|
||||
:name (path->name [:vendor/legal-entity-tin])
|
||||
:placeholder "SSN or EIN"
|
||||
:size "12"
|
||||
:value (-> vendor :vendor/legal-entity-tin)}]]]]
|
||||
[:div.level-item
|
||||
[:div.field
|
||||
[:p.help "TIN Type"]
|
||||
[:div.control
|
||||
[:div.select
|
||||
[:select {:name (path->name [:vendor/legal-entity-tin-type])}
|
||||
[:option {:value ""} ""]
|
||||
[:option {:value "ein" :selected (= (-> vendor :vendor/legal-entity-tin-type :db/ident) :legal-entity-tin-type/ein)} "EIN"]
|
||||
[:option {:value "ssn" :selected (= (-> vendor :vendor/legal-entity-tin-type :db/ident) :legal-entity-tin-type/ssn)} "SSN"]]]]]]
|
||||
[:div.level-item
|
||||
[:div.field
|
||||
[:p.help "1099 Type"]
|
||||
[:div.control
|
||||
[:div.select
|
||||
[:select {:name (path->name [:vendor/legal-entity-1099-type])}
|
||||
[:option {:value ""} ""]
|
||||
[:option {:value "none" :selected (= (-> vendor :vendor/legal-entity-1099-type :db/ident) :legal-entity-1099-type/none)} "None"]
|
||||
[:option {:value "misc" :selected (= (-> vendor :vendor/legal-entity-1099-type :db/ident) :legal-entity-1099-type/misc)} "Misc"]
|
||||
[:option {:value "landlord" :selected (= (-> vendor :vendor/legal-entity-1099-type :db/ident) :legal-entity-1099-type/landlord)} "Landlord"]]]]]]]]]
|
||||
[:button.button.is-primary "Save"])])))
|
||||
|
||||
|
||||
|
||||
(defn page [{:keys [identity matched-route] :as request}]
|
||||
(base-page
|
||||
[:div
|
||||
(table request)]
|
||||
[:div
|
||||
(company-side-bar matched-route)]))
|
||||
|
||||
15
src/clj/auto_ap/ssr/core.clj
Normal file
15
src/clj/auto_ap/ssr/core.clj
Normal file
@@ -0,0 +1,15 @@
|
||||
(ns auto-ap.ssr.core
|
||||
(:require
|
||||
[auto-ap.routes.utils
|
||||
:refer [wrap-admin wrap-client-redirect-unauthenticated wrap-secure]]
|
||||
[auto-ap.ssr.admin :as admin]
|
||||
[auto-ap.ssr.company.company-1099 :as company-1099]))
|
||||
|
||||
;; from auto-ap.ssr-routes, because they're shared
|
||||
(def key->handler {:admin-history (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin admin/history)))
|
||||
:admin-history-search (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin admin/history-search)))
|
||||
:admin-history-inspect (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin admin/inspect)))
|
||||
:company-1099 (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin company-1099/page)))
|
||||
:company-1099-vendor-dialog (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin company-1099/vendor-dialog)))
|
||||
:company-1099-vendor-save (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin company-1099/vendor-save)))})
|
||||
|
||||
109
src/clj/auto_ap/ssr/ui.clj
Normal file
109
src/clj/auto_ap/ssr/ui.clj
Normal file
@@ -0,0 +1,109 @@
|
||||
(ns auto-ap.ssr.ui
|
||||
(:require
|
||||
[auto-ap.logging :as alog]
|
||||
[config.core :refer [env]]
|
||||
[hiccup2.core :as hiccup]))
|
||||
|
||||
(defn html-page [hiccup]
|
||||
{:status 200
|
||||
:headers {"Content-Type" "text/html"}
|
||||
:body (str
|
||||
"<!DOCTYPE html>"
|
||||
(hiccup/html
|
||||
{}
|
||||
hiccup))})
|
||||
|
||||
(defn base-page [contents side-bar-contents]
|
||||
(html-page
|
||||
[:html.has-navbar-fixed-top
|
||||
[:head
|
||||
[:meta {:charset "utf-8"}]
|
||||
[:meta {:http-equiv "X-UA-Compatible", :content "IE=edge"}]
|
||||
[:meta {:name "viewport", :content "width=device-width, initial-scale=1"}]
|
||||
[:title "Integreat"]
|
||||
[:link {:rel "stylesheet", :href "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css", :integrity "sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=", :crossorigin "anonymous"}]
|
||||
[:link {:href "/css/font.min.css", :rel "stylesheet"}]
|
||||
[:link {:rel "stylesheet", :href "/css/bulma.min.css"}]
|
||||
[:link {:rel "stylesheet", :href "/css/bulma-calendar.min.css"}]
|
||||
[:link {:rel "stylesheet", :href "/css/bulma-badge.min.css"}]
|
||||
[:link {:rel "stylesheet", :href "/css/react-datepicker.min.inc.css"}]
|
||||
[:link {:rel "stylesheet", :href "/css/animate.css"}]
|
||||
[:link {:rel "stylesheet", :href "/finance-font/style.css"}]
|
||||
[:link {:rel "stylesheet", :href "/css/main.css"}]
|
||||
[:link {:rel "stylesheet", :href "https://unpkg.com/placeholder-loading/dist/css/placeholder-loading.min.css"}]
|
||||
[:script {:src "https://unpkg.com/hyperscript.org@0.9.7"}]
|
||||
[:script {:src "https://unpkg.com/htmx.org@1.8.4"
|
||||
:integrity "sha384-wg5Y/JwF7VxGk4zLsJEcAojRtlVp1FKKdGy1qN+OMtdq72WRvX/EdRdqg/LOhYeV"
|
||||
:crossorigin= "anonymous"}]
|
||||
[:script {:type "text/javascript", :src "https://cdn.yodlee.com/fastlink/v4/initialize.js", :async "async" }]]
|
||||
|
||||
|
||||
[:body
|
||||
[:div {:id "app"}
|
||||
[:div
|
||||
[:nav {:class "navbar has-shadow is-fixed-top is-grey"}
|
||||
|
||||
[:div {:class "container"}
|
||||
[:div {:class "navbar-brand"}
|
||||
[:a {:class "navbar-item", :href "../"}
|
||||
[:img {:src "/img/logo.png"}]]]
|
||||
[:div.navbar-menu {:id "navMenu"}
|
||||
[:div.navbar-start
|
||||
[:a.navbar-item {:href "/"}
|
||||
"Home" ]
|
||||
[:a.navbar-item {:href "/invoices/"}
|
||||
"Invoices" ]
|
||||
[:a.navbar-item {:href "/payments/"}
|
||||
"Payments" ]
|
||||
[:a.navbar-item {:href "/pos/sales-orders/"}
|
||||
"POS" ]
|
||||
[:a.navbar-item {:href "/transactions/"}
|
||||
"Transactions" ]
|
||||
|
||||
[:a.navbar-item {:href "/ledger/"}
|
||||
"Ledger" ]]]]]
|
||||
[:div {:class "columns has-shadow", :id "mail-app", :style "margin-bottom: 0px; height: calc(100vh - 46px);"}
|
||||
[:aside {:class "column aside menu is-2 "}
|
||||
[:div {:class "main left-nav"}
|
||||
side-bar-contents]]
|
||||
[:div {:class "column messages hero ", :id "message-feed", :style "overflow: auto;"}
|
||||
[:div {:class "inbox-messages"}
|
||||
contents]]]
|
||||
[:div]
|
||||
[:div {:id "dz-hidden"}]]]
|
||||
[:div#modal-holder]]]))
|
||||
|
||||
(defn html-response [hiccup & {:keys [status] :or {status 200}}]
|
||||
{:status status
|
||||
:headers {"Content-Type" "text/html"}
|
||||
:body (str
|
||||
(hiccup/html
|
||||
{}
|
||||
hiccup))})
|
||||
|
||||
(defn wrap-error-response [handler]
|
||||
(fn [request]
|
||||
(try
|
||||
(handler request)
|
||||
(catch Exception e
|
||||
(if-let [v (or (:validation-error (ex-data e))
|
||||
(:validation-error (ex-data (.getCause e))))]
|
||||
|
||||
(do
|
||||
(alog/warn ::request-validation-error
|
||||
:exception e)
|
||||
(html-response
|
||||
[:div.notification.is-warning.is-light
|
||||
v]
|
||||
:status 400))
|
||||
(do
|
||||
(alog/error ::request-error
|
||||
:exception e)
|
||||
(when (= "dev" (:dd-env env))
|
||||
(println e))
|
||||
(html-response
|
||||
[:div.notification.is-danger.is-light
|
||||
"Server error occured."
|
||||
(ex-message e)]
|
||||
:status 500)))))))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(ns auto-ap.client-routes)
|
||||
|
||||
(def routes ["/" {"" :index
|
||||
"login/" :login
|
||||
#"login/?" :login
|
||||
"needs-activation/" :needs-activation
|
||||
"needs-activation" :needs-activation
|
||||
"payments/" :payments
|
||||
@@ -42,3 +42,12 @@
|
||||
"balance-sheet" :balance-sheet
|
||||
"external" :external-ledger
|
||||
"external-import" :external-import-ledger}}])
|
||||
|
||||
(defn all-handle-keys [routes]
|
||||
(let [vals (vals routes)
|
||||
result (filter keyword? vals)
|
||||
deeper (filter map? vals)]
|
||||
(apply concat result
|
||||
(map all-handle-keys deeper))))
|
||||
|
||||
(def all-matches (set (all-handle-keys (second routes))))
|
||||
|
||||
2
src/cljc/auto_ap/shared_views.cljc
Normal file
2
src/cljc/auto_ap/shared_views.cljc
Normal file
@@ -0,0 +1,2 @@
|
||||
(ns auto-ap.shared-views)
|
||||
|
||||
101
src/cljc/auto_ap/shared_views/admin/side_bar.cljc
Normal file
101
src/cljc/auto_ap/shared_views/admin/side_bar.cljc
Normal file
@@ -0,0 +1,101 @@
|
||||
(ns auto-ap.shared-views.admin.side-bar
|
||||
(:require [bidi.bidi :as bidi]
|
||||
[auto-ap.client-routes :as client-routes]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
#?(:cljs [re-frame.core :as re-frame])
|
||||
#?(:cljs [reagent.core :as r])))
|
||||
|
||||
(defn deep-merge [v & vs]
|
||||
(letfn [(rec-merge [v1 v2]
|
||||
(if (and (map? v1) (map? v2))
|
||||
(merge-with deep-merge v1 v2)
|
||||
v2))]
|
||||
(when (some identity vs)
|
||||
(reduce #(rec-merge %1 %2) v vs))))
|
||||
(def all-client-visible-routes
|
||||
["/" (deep-merge ssr-routes/routes (second client-routes/routes))])
|
||||
|
||||
|
||||
(defn menu-item [{:keys [label route test-route active-route icon-class icon-style]}]
|
||||
[:p.menu-item
|
||||
[:a.item {:href (bidi/path-for all-client-visible-routes route)
|
||||
:class (when (test-route active-route) "is-active")}
|
||||
(if icon-style
|
||||
[:span {:class icon-class :style icon-style}]
|
||||
[:span {:class "icon"}
|
||||
[:i {:class icon-class}]])
|
||||
[:span {:class "name"} label]]])
|
||||
|
||||
(defn admin-side-bar-impl [active-route children]
|
||||
[:div
|
||||
[:p.menu-label "General"]
|
||||
(menu-item {:label "Dashboard"
|
||||
:icon-class "fa fa-tachometer"
|
||||
:test-route #{:admin}
|
||||
:active-route active-route
|
||||
:route :admin})
|
||||
|
||||
|
||||
[:p.menu-label "Setup"]
|
||||
(menu-item {:label "Clients"
|
||||
:icon-class "fa fa-star-o"
|
||||
:test-route #{:admin-clients
|
||||
:admin-specific-client
|
||||
:admin-specific-bank-account}
|
||||
:active-route active-route
|
||||
:route :admin-clients})
|
||||
(menu-item {:label "Vendors"
|
||||
:icon-class "fa fa-star-o"
|
||||
:test-route #{:admin-vendors}
|
||||
:active-route active-route
|
||||
:route :admin-vendors})
|
||||
(menu-item {:label "Users"
|
||||
:icon-class "icon icon-single-neutral-book"
|
||||
:test-route #{:admin-users}
|
||||
:active-route active-route
|
||||
:route :admin-users
|
||||
:icon-style {:font-size "25px"}})
|
||||
(menu-item {:label "Accounts"
|
||||
:icon-class "icon icon-list-bullets"
|
||||
:test-route #{:admin-accounts}
|
||||
:active-route active-route
|
||||
:route :admin-accounts
|
||||
:icon-style {:font-size "25px"}})
|
||||
(menu-item {:label "Rules"
|
||||
:icon-class "icon icon-cog-play-1"
|
||||
:test-route #{:admin-rules}
|
||||
:active-route active-route
|
||||
:route :admin-rules
|
||||
:icon-style {:font-size "25px"}})
|
||||
(menu-item {:label "History"
|
||||
:icon-class "icon icon-cog-play-1"
|
||||
:test-route #{:admin-history :admin-history-search :admin-history-inspect}
|
||||
:active-route active-route
|
||||
:route :admin-history
|
||||
:icon-style {:font-size "25px"}})
|
||||
[:p.menu-label "Import"]
|
||||
(menu-item {:label "Excel Invoices"
|
||||
:icon-class "fa fa-download"
|
||||
:test-route #{:admin-excel-import}
|
||||
:active-route active-route
|
||||
:route :admin-excel-import})
|
||||
(menu-item {:label "Excel Invoices"
|
||||
:icon-class "fa fa-download"
|
||||
:test-route #{:admin-import-batches}
|
||||
:active-route active-route
|
||||
:route :admin-import-batches})
|
||||
(menu-item {:label "Background Jobs"
|
||||
:icon-class "icon icon-cog-play-1"
|
||||
:test-route #{:admin-jobs}
|
||||
:active-route active-route
|
||||
:route :admin-jobs
|
||||
:icon-style {:font-size "25px"}})
|
||||
(into [:div ] children)])
|
||||
|
||||
#?(:clj
|
||||
(defn admin-side-bar [active-page]
|
||||
(admin-side-bar-impl active-page nil))
|
||||
:cljs
|
||||
(defn admin-side-bar []
|
||||
(admin-side-bar-impl @(re-frame/subscribe [:auto-ap.subs/active-page])
|
||||
(r/children (r/current-component)))))
|
||||
83
src/cljc/auto_ap/shared_views/company/sidebar.cljc
Normal file
83
src/cljc/auto_ap/shared_views/company/sidebar.cljc
Normal file
@@ -0,0 +1,83 @@
|
||||
(ns auto-ap.shared-views.company.sidebar
|
||||
(:require [bidi.bidi :as bidi]
|
||||
[auto-ap.client-routes :as client-routes]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
#?(:cljs [re-frame.core :as re-frame])
|
||||
#?(:cljs [reagent.core :as r])))
|
||||
|
||||
|
||||
(defn active-when [active-page f & rest]
|
||||
(when (apply f (into [active-page] rest)) " is-active"))
|
||||
|
||||
(defn deep-merge [v & vs]
|
||||
(letfn [(rec-merge [v1 v2]
|
||||
(if (and (map? v1) (map? v2))
|
||||
(merge-with deep-merge v1 v2)
|
||||
v2))]
|
||||
(when (some identity vs)
|
||||
(reduce #(rec-merge %1 %2) v vs))))
|
||||
|
||||
(def all-client-visible-routes
|
||||
["/" (deep-merge ssr-routes/routes (second client-routes/routes))])
|
||||
|
||||
(defn menu-item [{:keys [label route test-route active-route icon-class icon-style]}]
|
||||
[:p.menu-item
|
||||
[:a.item {:href (bidi/path-for all-client-visible-routes route)
|
||||
:class (when (test-route active-route) "is-active")}
|
||||
(if icon-style
|
||||
[:span {:class icon-class :style icon-style}]
|
||||
[:span {:class "icon"}
|
||||
[:i {:class icon-class}]])
|
||||
[:span {:class "name"} label]]])
|
||||
|
||||
(defn menu-item [{:keys [label route test-route active-route icon-class icon-style]}]
|
||||
[:p.menu-item
|
||||
[:a.item {:href (bidi/path-for all-client-visible-routes route)
|
||||
:class (when (test-route active-route) "is-active")}
|
||||
(if icon-style
|
||||
[:span {:class icon-class :style icon-style}]
|
||||
[:span {:class "icon"}
|
||||
[:i {:class icon-class}]])
|
||||
[:span {:class "name"} label]]])
|
||||
|
||||
(defn company-side-bar-impl [active-route]
|
||||
[:div
|
||||
(menu-item {:label "Reports"
|
||||
:route :reports
|
||||
:test-route #{:reports}
|
||||
:active-route active-route
|
||||
:icon-class "icon icon-receipt"
|
||||
:icon-style {:font-size "25px"}})
|
||||
|
||||
(menu-item {:label "Plaid Link"
|
||||
:route :plaid
|
||||
:test-route #{:plaid}
|
||||
:active-route active-route
|
||||
:icon-class "icon icon-saving-bank-1"
|
||||
:icon-style {:font-size "25px"}})
|
||||
(menu-item {:label "Yodlee Link"
|
||||
:route :yodlee2
|
||||
:test-route #{:yodlee2}
|
||||
:active-route active-route
|
||||
:icon-class "icon icon-saving-bank-1"
|
||||
:icon-style {:font-size "25px"}})
|
||||
|
||||
(menu-item {:label "Other"
|
||||
:route :company-other
|
||||
:test-route #{:company-other}
|
||||
:active-route active-route
|
||||
:icon-class "icon icon-cog-play-1"
|
||||
:icon-style {:font-size "25px"}})
|
||||
(menu-item {:label "1099 Info"
|
||||
:route :company-1099
|
||||
:test-route #{:company-1099}
|
||||
:active-route active-route
|
||||
:icon-class "icon icon-cog-play-1"
|
||||
:icon-style {:font-size "25px"}})])
|
||||
|
||||
#?(:clj
|
||||
(defn company-side-bar [active-page]
|
||||
(company-side-bar-impl active-page))
|
||||
:cljs
|
||||
(defn company-side-bar []
|
||||
(company-side-bar-impl @(re-frame/subscribe [:auto-ap.subs/active-page]))))
|
||||
14
src/cljc/auto_ap/ssr_routes.cljc
Normal file
14
src/cljc/auto_ap/ssr_routes.cljc
Normal file
@@ -0,0 +1,14 @@
|
||||
(ns auto-ap.ssr-routes)
|
||||
|
||||
(def routes {"admin" {"/history" {"" :admin-history
|
||||
"/" :admin-history
|
||||
#"/search/?" :admin-history-search
|
||||
["/" [#"\d+" :entity-id] #"/?"] :admin-history-search
|
||||
["/inspect/" [#"\d+" :entity-id] #"/?"] :admin-history-inspect}}
|
||||
"company" {"/1099" :company-1099
|
||||
"/1099/vendor-dialog" {["/" [#"\d+" :vendor-id]] {:get :company-1099-vendor-dialog
|
||||
:post :company-1099-vendor-save}}}})
|
||||
|
||||
|
||||
(def only-routes ["/" routes])
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
(ns auto-ap.views.components.admin.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]
|
||||
[reagent.core :as r]))
|
||||
|
||||
(defn admin-side-bar []
|
||||
(let [ap @(re-frame/subscribe [::subs/active-page])]
|
||||
[:div
|
||||
[:p.menu-label "General"]
|
||||
[:p.menu-item
|
||||
[:a {:href (bidi/path-for routes/routes :admin) , :class (str "item" (active-when ap = :admin))}
|
||||
[:span {:class "icon"}
|
||||
[:i {:class "fa fa-tachometer"}]]
|
||||
[:span {:class "name"} "Dashboard"]]]
|
||||
|
||||
[:p.menu-label "Setup"]
|
||||
[:ul.menu-list
|
||||
[:li.menu-item
|
||||
[:a {:href (bidi/path-for routes/routes :admin-clients) , :class (str "item" (active-when ap = :admin-clients))}
|
||||
[:span {:class "icon"}
|
||||
[:i {:class "fa fa-star-o"}]]
|
||||
|
||||
[:span {:class "name"} "Clients"]]]
|
||||
[:li.menu-item
|
||||
[:a {:href (bidi/path-for routes/routes :admin-vendors) , :class (str "item" (active-when ap = :admin-vendors))}
|
||||
[:span {:class "icon"}
|
||||
[:i {:class "fa fa-star-o"}]]
|
||||
|
||||
[:span {:class "name"} "Vendors"]]]
|
||||
[:li.menu-item
|
||||
[:a {:href (bidi/path-for routes/routes :admin-users), :class (str "item" (active-when ap = :admin-users))}
|
||||
[:span {:class "icon icon-single-neutral-book" :style {:font-size "25px"}}]
|
||||
[:span {:class "name"} "Users"]]]
|
||||
|
||||
[:li.menu-item
|
||||
[:a {:href (bidi/path-for routes/routes :admin-accounts), :class (str "item" (active-when ap = :admin-accounts))}
|
||||
[:span {:class "icon icon-list-bullets" :style {:font-size "25px"}}]
|
||||
[:span {:class "name"} "Accounts"]]]
|
||||
|
||||
[:li.menu-item
|
||||
[:a {:href (bidi/path-for routes/routes :admin-rules), :class (str "item" (active-when ap = :admin-rules))}
|
||||
[:span {:class "icon icon-cog-play-1" :style {:font-size "25px"}}]
|
||||
[:span {:class "name"} "Rules"]]]
|
||||
[:li.menu-item
|
||||
[:a {:href (str "/admin/history") :class (str "item" (active-when ap = :admin-history))}
|
||||
[:span {:class "icon icon-cog-play-1" :style {:font-size "25px"}}]
|
||||
[:span {:class "name"} "History "]]]
|
||||
[:ul ]]
|
||||
[:p.menu-label "Import"]
|
||||
[:ul.menu-list
|
||||
[:li.menu-item
|
||||
[:a {:href (bidi/path-for routes/routes :admin-excel-import) , :class (str "item" (active-when ap = :admin-excel-import))}
|
||||
[:span {:class "icon"}
|
||||
[:i {:class "fa fa-download"}]]
|
||||
|
||||
[:span {:class "name"} "Excel Invoices"]]]
|
||||
[:li.menu-item
|
||||
[:a {:href (bidi/path-for routes/routes :admin-import-batches) , :class (str "item" (active-when ap = :admin-import-batches))}
|
||||
[:span {:class "icon"}
|
||||
[:i {:class "fa fa-download"}]]
|
||||
|
||||
[:span {:class "name"} "Import Batches"]]]
|
||||
[:li.menu-item
|
||||
[:a {:href (bidi/path-for routes/routes :admin-jobs) :class (str "item" (active-when ap = :admin-jobs))}
|
||||
[:span {:class "icon icon-cog-play-1" :style {:font-size "25px"}}]
|
||||
[:span {:class "name"} "Background Jobs"]]]]
|
||||
|
||||
(into [:div ] (r/children (r/current-component)))]))
|
||||
@@ -166,6 +166,10 @@
|
||||
(when-not is-initial-loading
|
||||
[:div.navbar-end
|
||||
|
||||
[:a.navbar-item {:href "/company/1099"}
|
||||
[:div.tag.is-info.is-rounded
|
||||
"1099 data entry is now ready!"]]
|
||||
|
||||
(when (> (count @clients) 1)
|
||||
[client-dropdown]
|
||||
)])]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns auto-ap.views.pages.admin
|
||||
(:require
|
||||
[auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.views.components.layouts :refer [side-bar-layout]]))
|
||||
|
||||
(defn admin-page []
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
(:require
|
||||
[auto-ap.effects.forward :as forward]
|
||||
[auto-ap.forms :as forms]
|
||||
[auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.views.components.buttons :as buttons]
|
||||
[auto-ap.views.components.layouts
|
||||
:refer [appearing-side-bar side-bar-layout]]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[auto-ap.routes :as routes]
|
||||
[auto-ap.status :as status]
|
||||
[auto-ap.subs :as subs]
|
||||
[auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.views.components.grid :as grid]
|
||||
[auto-ap.views.components.layouts :refer [side-bar-layout]]
|
||||
[auto-ap.views.pages.admin.clients.form :as form]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[auto-ap.forms.builder :as form-builder]
|
||||
[auto-ap.schema :as schema]
|
||||
[auto-ap.views.components :as com]
|
||||
[auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.views.components.layouts :refer [side-bar-layout]]
|
||||
[auto-ap.views.utils :refer [with-user]]
|
||||
[malli.core :as m]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(ns auto-ap.views.pages.admin.import-batches
|
||||
(:require
|
||||
[auto-ap.subs :as subs]
|
||||
[auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.views.components.layouts :refer [side-bar-layout]]
|
||||
[auto-ap.views.pages.admin.import-batches.table :as table]
|
||||
[auto-ap.views.pages.data-page :as data-page]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
(:require
|
||||
[auto-ap.status :as status]
|
||||
[auto-ap.subs :as subs]
|
||||
[auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.views.components.layouts :refer [side-bar-layout]]
|
||||
[auto-ap.views.pages.admin.jobs.table :as table]
|
||||
[auto-ap.views.components.modal :as modal]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[auto-ap.events :as events]
|
||||
[auto-ap.forms :as forms]
|
||||
[auto-ap.subs :as subs]
|
||||
[auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.views.components.buttons :as buttons]
|
||||
[auto-ap.views.components.layouts
|
||||
:refer [appearing-side-bar side-bar-layout]]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[auto-ap.effects.forward :as forward]
|
||||
[auto-ap.status :as status]
|
||||
[auto-ap.utils :refer [replace-by]]
|
||||
[auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.views.components.grid :as grid]
|
||||
[auto-ap.views.components.layouts :refer [side-bar-layout]]
|
||||
[auto-ap.views.pages.admin.users.form :as form]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
(:require
|
||||
[auto-ap.effects.forward :as forward]
|
||||
[auto-ap.subs :as subs]
|
||||
[auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.views.components.layouts :refer [side-bar-layout]]
|
||||
[auto-ap.views.pages.admin.vendors.merge-dialog :as merge-dialog]
|
||||
[auto-ap.views.pages.admin.vendors.side-bar :as side-bar]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[auto-ap.views.utils :refer [dispatch-event with-user]]
|
||||
[auto-ap.views.components.buttons :refer [fa-icon]]
|
||||
[auto-ap.views.components.layouts :refer [side-bar-layout]]
|
||||
[auto-ap.views.pages.company.side-bar :refer [company-side-bar]]
|
||||
[auto-ap.shared-views.company.sidebar :refer [company-side-bar]]
|
||||
[goog.crypt.base64 :as b64]
|
||||
[re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[auto-ap.subs :as subs]
|
||||
[auto-ap.views.components.layouts :refer [side-bar-layout]]
|
||||
[auto-ap.views.pages.admin.plaid.table :as table]
|
||||
[auto-ap.views.pages.company.side-bar :refer [company-side-bar]]
|
||||
[auto-ap.shared-views.company.sidebar :refer [company-side-bar]]
|
||||
[auto-ap.views.pages.data-page :as data-page]
|
||||
[auto-ap.views.utils :refer [dispatch-event with-user]]
|
||||
[clojure.set :as set]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[auto-ap.subs :as subs]
|
||||
[auto-ap.views.components.grid :as grid]
|
||||
[auto-ap.views.components.layouts :refer [side-bar-layout]]
|
||||
[auto-ap.views.pages.company.side-bar :refer [company-side-bar]]
|
||||
[auto-ap.shared-views.company.sidebar :refer [company-side-bar]]
|
||||
[auto-ap.views.pages.company.yodlee2.table :as table]
|
||||
[auto-ap.views.utils :refer [dispatch-event with-user]]
|
||||
[re-frame.core :as re-frame]
|
||||
|
||||
@@ -108,7 +108,6 @@
|
||||
(re-frame/inject-cofx ::inject/sub (fn [[_ _ _ client]]
|
||||
[::subs/locations-for-client (:id client)]))]
|
||||
(fn [{:keys [db] ::subs/keys [locations-for-client]} [_ _ command client]]
|
||||
(println locations-for-client)
|
||||
(when (= :create command)
|
||||
{:db
|
||||
(-> db
|
||||
|
||||
@@ -3,7 +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.pages.company.side-bar
|
||||
[auto-ap.shared-views.company.sidebar
|
||||
:as side-bar
|
||||
:refer [company-side-bar]]
|
||||
[auto-ap.views.pages.reports.table :as table]
|
||||
|
||||
Reference in New Issue
Block a user