From 8234db562360a1f71d1449936db5d74813f7ea84 Mon Sep 17 00:00:00 2001 From: Bryce Date: Mon, 7 Oct 2024 20:55:30 -0700 Subject: [PATCH] Highlights plaid errors more clearly --- iol_ion/src/iol_ion/query.clj | 30 ++++++++ src/clj/auto_ap/plaid/core.clj | 88 ++++++++++++++------- src/clj/auto_ap/ssr/company/plaid.clj | 106 ++++++++++++++++++-------- src/clj/user.fiddle | 72 +++++++++++++++++ 4 files changed, 237 insertions(+), 59 deletions(-) diff --git a/iol_ion/src/iol_ion/query.clj b/iol_ion/src/iol_ion/query.clj index c6fbb311..f89f5dc5 100644 --- a/iol_ion/src/iol_ion/query.clj +++ b/iol_ion/src/iol_ion/query.clj @@ -179,6 +179,7 @@ :when id] [client-id account-id location date current-balance])) +(comment #_(account-snapshot (dc/db auto-ap.datomic/conn) (auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn) [:client/code "NGOP"]) @@ -190,3 +191,32 @@ [(iol-ion.query/account-snapshot $ ?c #inst "2023-01-01") [?x ...]] [(untuple ?x) [_ ?a ?l ?date ?balance]]] (dc/db auto-ap.datomic/conn))) + +#_(->> (seq (dc/q '[:find ?code ?name ?afc ?an ?l ?d2 ?balance ?end4 + :in $ ?end ?group + :where + [(clj-time.coerce/to-date-time ?end) ?end2] + [(iol-ion.query/localize ?end2) ?end3] + [(clj-time.coerce/to-date ?end3) ?end4] + (or + [?c :client/groups ?group] + [?c :client/code ?group]) + [?c :client/name ?name] + [?c :client/code ?code] + [?c :client/bank-accounts ?b] + [(iol-ion.query/account-snapshot $ ?c ?end4) [?x ...]] + [(untuple ?x) [_ ?a ?l ?date ?balance]] + [(iol-ion.query/excel-date ?date) ?d2] + [(not= nil ?a)] + (or-join [?a ?afc ?an] + (and [?a :account/name ?an] + [?a :account/numeric-code ?afc]) + (and [?a :bank-account/name ?an] + [?a :bank-account/numeric-code ?afc]))] + (dc/db auto-ap.datomic/conn) + #inst "2024-09-23" + "NGKG")) + (filter (fn [[_ _ afc]] + (= 12990 afc))) + (map (fn [[_ _ _ _ _ _ a]] + (Math/round a))))) \ No newline at end of file diff --git a/src/clj/auto_ap/plaid/core.clj b/src/clj/auto_ap/plaid/core.clj index 3200d2df..0439e34d 100644 --- a/src/clj/auto_ap/plaid/core.clj +++ b/src/clj/auto_ap/plaid/core.clj @@ -4,7 +4,8 @@ [cemerick.url :as url] [clj-http.client :as client] [clojure.data.json :as json] - [config.core :as cfg :refer [env]])) + [config.core :as cfg :refer [env]] + [slingshot.slingshot :refer [try+]])) (def base-url (-> env :plaid :base-url)) @@ -50,23 +51,43 @@ "public_token" public-token})}) :body)) -(defn get-item [access-token ] - (-> (client/post (str base-url "/item/get") - {:as :json - :headers {"Content-Type" "application/json"} - :body (json/write-str {"client_id" client-id - "secret" secret-key - "access_token" access-token})}) - :body)) +(defn get-item [access-token] + (try+ + (-> (client/post (str base-url "/item/get") + {:as :json + :headers {"Content-Type" "application/json"} + :body (json/write-str {"client_id" client-id + "secret" secret-key + "access_token" access-token})}) + :body) + (catch [:status 400] x + (let [json (try (json/read-str (:body x)) (catch Exception _ + {}))] + (throw (ex-info + (or (get json "error_message") + (get json "display_message") + (.getMessage (:throwable &throw-context))) + json)))))) + +(defn get-accounts [access-token] + (try+ + (-> (client/post (str base-url "/accounts/get") + {:as :json + :headers {"Content-Type" "application/json"} + :body (json/write-str {"client_id" client-id + "secret" secret-key + "access_token" access-token})}) + :body) + (catch [:status 400] x + (let [json (try (json/read-str (:body x)) (catch Exception _ + {}))] + (throw (ex-info + (or (get json "error_message") + (get json "display_message") + (.getMessage (:throwable &throw-context))) + json)))))) + -(defn get-accounts [access-token ] - (-> (client/post (str base-url "/accounts/get") - {:as :json - :headers {"Content-Type" "application/json"} - :body (json/write-str {"client_id" client-id - "secret" secret-key - "access_token" access-token})}) - :body)) (defn get-balance [access-token ] (-> (client/post (str base-url "/accounts/balance/get") @@ -82,17 +103,28 @@ :start (str start) :end (str end) :acct (str account-id)) - (-> (client/post (str base-url "/transactions/get") - {:as :json - :headers {"Content-Type" "application/json"} - :body (json/write-str {"client_id" client-id - "secret" secret-key - "access_token" access-token - "start_date" (atime/unparse start atime/iso-date) - "end_date" (atime/unparse end atime/iso-date) - "options" {"account_ids" [account-id] - "count" 500}})}) - :body)) + + + (try+ + (-> (client/post (str base-url "/transactions/get") + {:as :json + :headers {"Content-Type" "application/json"} + :body (json/write-str {"client_id" client-id + "secret" secret-key + "access_token" access-token + "start_date" (atime/unparse start atime/iso-date) + "end_date" (atime/unparse end atime/iso-date) + "options" {"account_ids" [account-id] + "count" 500}})}) + :body) + (catch [:status 400] x + (let [json (try (json/read-str (:body x)) (catch Exception _ + {}))] + (throw (ex-info + (or (get json "error_message") + (get json "display_message") + (.getMessage (:throwable &throw-context))) + json)))))) (comment (require '[datomic.api :as dc]) diff --git a/src/clj/auto_ap/ssr/company/plaid.clj b/src/clj/auto_ap/ssr/company/plaid.clj index 78a27ff4..13a86e66 100644 --- a/src/clj/auto_ap/ssr/company/plaid.clj +++ b/src/clj/auto_ap/ssr/company/plaid.clj @@ -1,35 +1,36 @@ (ns auto-ap.ssr.company.plaid - (:require - [auto-ap.datomic - :refer [add-sorter-fields - apply-pagination - apply-sort-3 - conn - merge-query - pull-attr - pull-many-by-id - query2]] - [auto-ap.graphql.utils :refer [assert-can-see-client]] - [auto-ap.logging :as alog] - [auto-ap.plaid.core :as p] - [auto-ap.ssr-routes :as ssr-routes] - [auto-ap.ssr.components :as com] - [auto-ap.ssr.grid-page-helper :as helper] - [auto-ap.ssr.svg :as svg] - [auto-ap.ssr.utils :refer [html-response]] - [auto-ap.time :as atime] - [bidi.bidi :as bidi] - [clj-time.coerce :as coerce] - [clj-time.core :as time] - [datomic.api :as dc] - [hiccup2.core :as hiccup])) + (:require [auto-ap.datomic + :refer [add-sorter-fields apply-pagination apply-sort-3 conn merge-query + pull-attr pull-many-by-id query2]] + [auto-ap.graphql.utils :refer [assert-can-see-client]] + [auto-ap.logging :as alog] + [auto-ap.plaid.core :as p] + [auto-ap.ssr-routes :as ssr-routes] + [auto-ap.ssr.components :as com] + [auto-ap.ssr.grid-page-helper :as helper] + [auto-ap.ssr.hx :as hx] + [auto-ap.ssr.svg :as svg] + [auto-ap.ssr.utils :refer [html-response]] + [auto-ap.time :as atime] + [bidi.bidi :as bidi] + [clj-time.coerce :as coerce] + [clj-time.core :as time] + [clojure.string :as str] + [datomic.api :as dc] + [hiccup2.core :as hiccup])) (def default-read '[:db/id :plaid-item/external-id :plaid-item/access-token :plaid-item/last-updated :plaid-item/status + {:plaid-item/accounts [:db/id + {:bank-account/_plaid-account [{:bank-account/integration-status + [{ [ :integration-status/state :xform iol-ion.query/ident] [:db/ident]} + :integration-status/message + :integration-status/last-attempt + :integration-status/last-updated]}]} :plaid-account/external-id :plaid-account/number :plaid-account/balance @@ -169,14 +170,57 @@ :name "Plaid Item" :sort-key "external-id" :render :plaid-item/external-id} - {:key "status" - :name "Status" - :sort-key "status" - :render #(when-let [status (:plaid-item/status %)] - [:div [:div (com/pill {:color :primary} - status)] - [:div (atime/unparse-local (coerce/to-date-time (:plaid-item/last-updated %)) atime/normal-date)]])} + {:key "integreat-plaid-status" + :name "Integreat ↔ Plaid status" + :sort-key "integreat-plaid-status" + :render (fn [e] + + (let [bad-integration (->> (:plaid-item/accounts e) + (map (comp + first + :bank-account/_plaid-account)) + (filter (comp #{:integration-state/failed :integration-state/unauthorized} + :integration-status/state + :bank-account/integration-status)) + first + :bank-account/integration-status)] + [:div + (when bad-integration + {:x-popper (hx/json {:source "$refs.button" + :tooltip "$refs.tooltip"}) + :x-data (hx/json {}) + }) + [:div.cursor-pointer (com/pill {:color (if bad-integration + :red + :primary) :x-ref "button"} + [:div.inline-flex.gap-2 + (or + (some-> bad-integration + :integration-status/state + name + str/capitalize) + "Success") + (when bad-integration + " (detail)") + + + (when bad-integration + (com/tooltip {:x-ref "tooltip"} + [:div.text-red-700 + (:integration-status/message bad-integration)]))])] + [:div.grid.grid-cols-2.gap-1.auto-cols-min.grid-flow-row.shrink + [:div "Attempted: "] [:div (atime/unparse-local (coerce/to-date-time (:integration-status/last-attempt e)) atime/normal-date)] + [:div "Last Updated: "] [:div (atime/unparse-local (coerce/to-date-time (:integration-status/last-updated e)) atime/normal-date)]]]))} + {:key "plaid-bank-status" + :name "Plaid ↔ Bank Status" + :sort-key "plaid-bank-status" + :render (fn [e] + (when-let [status (:plaid-item/status e)] + [:div [:div (com/pill {:color :primary} + status)] + [:div (atime/unparse-local (coerce/to-date-time (:plaid-item/last-updated e)) atime/normal-date)]]))} + {:key "accounts" :name "Accounts" :show-starting "md" diff --git a/src/clj/user.fiddle b/src/clj/user.fiddle index f480679d..16a97c67 100644 --- a/src/clj/user.fiddle +++ b/src/clj/user.fiddle @@ -753,3 +753,75 @@ (assoc :dirty-entries (auto-ap.ledger/get-dirty-entries account-needing-rebuild db)) (assoc :account-type (:account_type ((auto-ap.ledger/build-account-lookup (:client account-needing-rebuild)) (:account account-needing-rebuild)))) (auto-ap.ledger/compute-running-balance))]) + + +(seq + (datomic.api/q (quote + [:find ?code ?name ?bn ?bac ?bt2 ?d2 ?bas +:in $ ?start ?end ?group +:where +(or + [?c :client/groups ?group] + [?c :client/code ?group]) +[?c :client/name ?name] +[?c :client/code ?code] +[?c :client/bank-accounts ?b] +[?b :bank-account/name ?bn] +[?b :bank-account/type ?bat] +[?b :bank-account/code ?bac] +[?bat :db/ident ?bt] +[(name ?bt) ?bt2] +[(vector ?c) ?c2] +(or-join [?b ?bas] + (and [?b :bank-account/plaid-account] + [(ground "plaid") ?bas]) + (and [?b :bank-account/intuit-bank-account] + [(ground "intuit") ?bas]) + (and [?b :bank-account/yodlee-account] + [(ground "yodlee") ?bas]) + (and (not [?b :bank-account/yodlee-account]) + (not [?b :bank-account/intuit-bank-account]) + (not [?b :bank-account/plaid-account]) + [(ground "none") ?bas])) +[(datomic.api/q (quote [:find ?b (max ?d) + :in $ ?c2 ?start ?end + :where [(iol-ion.query/scan-transactions $ ?c2 ?start ?end) [[?t _ _] ...]] + [?t :transaction/date ?d] + [?t :transaction/bank-account ?b]]) + $ ?c2 ?start ?end) + [[?b ?d] ...]] +[(iol-ion.query/excel-date ?d) ?d2]]) + (dc/db conn) + #inst "2024-09-01" + #inst "2024-09-30" + "NTG")) + +(user/init-repl) + + +(seq + (datomic.api/q (quote + [:find ?code ?name ?bn ?bac ?bt2 ?as3 (count ?t) +:in $ ?start ?end ?group +:where +(or + [?c :client/groups ?group] + [?c :client/code ?group]) +[?c :client/name ?name] +[?c :client/code ?code] +[?c :client/bank-accounts ?b] +[?b :bank-account/name ?bn] +[?b :bank-account/type ?bat] +[?b :bank-account/code ?bac] +[?bat :db/ident ?bt] +[(name ?bt) ?bt2] +[(vector ?c) ?c2] +[(iol-ion.query/scan-transactions $ ?c2 ?start ?end) [[?t _ _] ...]] +[?t :transaction/approval-status ?as] +[?as :db/ident ?as2] +[(name ?as2) ?as3] + ]) + (dc/db conn) + #inst "2024-09-01" + #inst "2024-09-30" + "NTG"))