merged.
This commit is contained in:
@@ -1,25 +1,37 @@
|
||||
(ns auto-ap.import.intuit
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn]]
|
||||
[auto-ap.import.common :refer [wrap-integration]]
|
||||
[auto-ap.import.transactions :as t]
|
||||
[auto-ap.intuit.core :as i]
|
||||
[auto-ap.logging :as alog]
|
||||
[auto-ap.time :as atime]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clj-time.core :as time]
|
||||
[clojure.string :as str]
|
||||
[com.unbounce.dogstatsd.core :as statsd]
|
||||
[datomic.api :as dc]))
|
||||
(:require [auto-ap.datomic :refer [conn]]
|
||||
[auto-ap.import.common :refer [wrap-integration]]
|
||||
[auto-ap.import.transactions :as t]
|
||||
[auto-ap.intuit.core :as i]
|
||||
[auto-ap.logging :as alog]
|
||||
[auto-ap.time :as atime]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clj-time.core :as time]
|
||||
[clojure.string :as str]
|
||||
[com.unbounce.dogstatsd.core :as statsd]
|
||||
[datomic.api :as dc]
|
||||
[iol-ion.utils :refer [remove-nils]]))
|
||||
|
||||
(defn get-intuit-bank-accounts
|
||||
( [db]
|
||||
(dc/q '[:find ?external-id ?ba ?c
|
||||
:in $
|
||||
:where
|
||||
[?c :client/bank-accounts ?ba]
|
||||
[?ba :bank-account/intuit-bank-account ?iab]
|
||||
[?iab :intuit-bank-account/external-id ?external-id]]
|
||||
db))
|
||||
([db & client-codes]
|
||||
(dc/q '[:find ?external-id ?ba ?c
|
||||
:in $ [?cc ...]
|
||||
:where
|
||||
[?c :client/code ?cc]
|
||||
[?c :client/bank-accounts ?ba]
|
||||
[?ba :bank-account/intuit-bank-account ?iab]
|
||||
[?iab :intuit-bank-account/external-id ?external-id]]
|
||||
db
|
||||
client-codes)))
|
||||
|
||||
(defn get-intuit-bank-accounts [db]
|
||||
(dc/q '[:find ?external-id ?ba ?c
|
||||
:in $
|
||||
:where
|
||||
[?c :client/bank-accounts ?ba]
|
||||
[?ba :bank-account/intuit-bank-account ?iab]
|
||||
[?iab :intuit-bank-account/external-id ?external-id]]
|
||||
db))
|
||||
|
||||
(defn intuit->transaction [transaction]
|
||||
(let [check-number (when (not (str/blank? (:Num transaction)))
|
||||
@@ -78,6 +90,9 @@
|
||||
bank-accounts (i/get-bank-accounts token)]
|
||||
@(dc/transact conn (mapv
|
||||
(fn [ba]
|
||||
{:intuit-bank-account/external-id (:name ba)
|
||||
:intuit-bank-account/name (:name ba)})
|
||||
(remove-nils
|
||||
{:intuit-bank-account/external-id (:name ba)
|
||||
:intuit-bank-account/name (:name ba)
|
||||
:intuit-bank-account/last-synced (coerce/to-date (:last-updated ba))
|
||||
:intuit-bank-account/current-balance (:current-balance ba)}))
|
||||
bank-accounts))))
|
||||
|
||||
@@ -10,10 +10,64 @@
|
||||
[clj-time.core :as time]
|
||||
[clojure.string :as str]
|
||||
[com.unbounce.dogstatsd.core :as statsd]
|
||||
[datomic.api :as dc]))
|
||||
[datomic.api :as dc]
|
||||
[clj-http.client :as client]))
|
||||
|
||||
(for [[e] (take 5 (get-intuit-bank-accounts (dc/db conn)))]
|
||||
|
||||
(i/get-transactions "2023-02-01"
|
||||
"2023-02-05"
|
||||
e))
|
||||
(defn get-bank-accounts [token]
|
||||
|
||||
(defn get-bank-accounts [token]
|
||||
(->> (:body (client/get (str i/prod-base-url "/company/" i/prod-company-id "/query")
|
||||
{:headers
|
||||
(i/with-auth i/prod-base-headers token)
|
||||
:as :json
|
||||
:query-params {"query" "SELECT * From Account maxresults 1000"}}))
|
||||
:QueryResponse
|
||||
:Account
|
||||
#_(filter
|
||||
#(#{"Bank" "Credit Card"} (:AccountType %))))))
|
||||
|
||||
|
||||
|
||||
(require 'auto-ap.time_reader)
|
||||
|
||||
(let [start #clj-time/date-time "2024-02-01"
|
||||
end #clj-time/date-time "2024-04-01"]
|
||||
(for [[ib ba c] (seq (get-intuit-bank-accounts (dc/db conn) "BCFM"))
|
||||
:let [raw-transactions (i/get-transactions (atime/unparse-local start atime/iso-date)
|
||||
(atime/unparse-local end atime/iso-date)
|
||||
ib)
|
||||
ideal-transactions (intuits->transactions raw-transactions ba c)
|
||||
|
||||
found-transactions (when (seq ideal-transactions)
|
||||
(into {} (dc/q '[:find ?si (count ?t)
|
||||
:in $ [?eid ...]
|
||||
:where
|
||||
[?t :transaction/id ?eid]
|
||||
[?t :transaction/approval-status ?s]
|
||||
[?s :db/ident ?si]]
|
||||
(dc/db conn)
|
||||
(map :transaction/id ideal-transactions))))
|
||||
|
||||
missing-transaction-ids (when (seq ideal-transactions)
|
||||
(->>
|
||||
(dc/q '[:find ?eid
|
||||
:in $ [?eid ...]
|
||||
:where (not [_ :transaction/id ?eid])]
|
||||
(dc/db conn)
|
||||
(map :transaction/id ideal-transactions))
|
||||
(map first)
|
||||
(into #{})))
|
||||
missing-transactions (filter (comp missing-transaction-ids :transaction/id) ideal-transactions)]]
|
||||
{:bank-account/name (pull-attr (dc/db conn) :bank-account/name ba)
|
||||
:external-transaction-count (count raw-transactions)
|
||||
:integreat-transaction-count (reduce + 0 (vals found-transactions))
|
||||
:approved-count (:transaction-approval-status/approved found-transactions 0)
|
||||
:unapproved-count (:transaction-approval-status/unapproved found-transactions 0)
|
||||
:requires-feedback-count (:transaction-approval-status/requires-feedback found-transactions 0)
|
||||
:missing-transactions missing-transactions}))
|
||||
|
||||
|
||||
@@ -1,31 +1,44 @@
|
||||
(ns auto-ap.import.plaid
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn random-tempid]]
|
||||
[auto-ap.import.common :refer [wrap-integration]]
|
||||
[auto-ap.import.transactions :as t]
|
||||
[auto-ap.logging :as alog]
|
||||
[auto-ap.plaid.core :as p]
|
||||
[auto-ap.solr]
|
||||
[auto-ap.time :as atime]
|
||||
[auto-ap.utils :refer [allow-once by]]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clj-time.core :as time]
|
||||
[datomic.api :as dc]
|
||||
[digest :as di]
|
||||
[manifold.deferred :as de]
|
||||
[manifold.executor :as ex]
|
||||
[clojure.string :as str]))
|
||||
(:require [auto-ap.datomic :refer [conn random-tempid]]
|
||||
[auto-ap.import.common :refer [wrap-integration]]
|
||||
[auto-ap.import.transactions :as t]
|
||||
[auto-ap.logging :as alog]
|
||||
[auto-ap.plaid.core :as p]
|
||||
[auto-ap.solr]
|
||||
[auto-ap.time :as atime]
|
||||
[auto-ap.utils :refer [allow-once by]]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clj-time.core :as time]
|
||||
[clojure.string :as str]
|
||||
[datomic.api :as dc]
|
||||
[digest :as di]
|
||||
[iol-ion.utils :refer [remove-nils]]
|
||||
[manifold.deferred :as de]
|
||||
[manifold.executor :as ex]))
|
||||
|
||||
(defn get-plaid-accounts [db]
|
||||
(-> (dc/q '[:find ?ba ?c ?external-id ?t
|
||||
:in $
|
||||
:where
|
||||
[?c :client/bank-accounts ?ba]
|
||||
[?ba :bank-account/plaid-account ?pa]
|
||||
[?pa :plaid-account/external-id ?external-id]
|
||||
[?pi :plaid-item/accounts ?pa]
|
||||
[?pi :plaid-item/access-token ?t]]
|
||||
db )))
|
||||
(defn get-plaid-accounts
|
||||
([db]
|
||||
(-> (dc/q '[:find ?ba ?c ?external-id ?t
|
||||
:in $
|
||||
:where
|
||||
[?c :client/bank-accounts ?ba]
|
||||
[?ba :bank-account/plaid-account ?pa]
|
||||
[?pa :plaid-account/external-id ?external-id]
|
||||
[?pi :plaid-item/accounts ?pa]
|
||||
[?pi :plaid-item/access-token ?t]]
|
||||
db)))
|
||||
([db & client-codes]
|
||||
(-> (dc/q '[:find ?ba ?c ?external-id ?t
|
||||
:in $ [?cc ...]
|
||||
:where
|
||||
[?c :client/code ?cc]
|
||||
[?c :client/bank-accounts ?ba]
|
||||
[?ba :bank-account/plaid-account ?pa]
|
||||
[?pa :plaid-account/external-id ?external-id]
|
||||
[?pi :plaid-item/accounts ?pa]
|
||||
[?pi :plaid-item/access-token ?t]]
|
||||
db
|
||||
client-codes))))
|
||||
|
||||
|
||||
(defn plaid->transaction [t plaid-merchant->vendor-id]
|
||||
@@ -71,8 +84,36 @@
|
||||
{"id" (:db/id result)
|
||||
"name" (:plaid-merchant/name result)}))))
|
||||
|
||||
(defn upsert-accounts []
|
||||
(try
|
||||
(doseq [[bank-account-id client-id external-id access-token] (get-plaid-accounts (dc/db conn))]
|
||||
(try
|
||||
(let [accounts (p/get-accounts access-token)
|
||||
item (p/get-item access-token)]
|
||||
@(dc/transact
|
||||
conn
|
||||
(for [a (:accounts accounts)]
|
||||
(remove-nils
|
||||
{:plaid-account/external-id (:account_id a)
|
||||
:plaid-account/last-synced (coerce/to-date (coerce/to-date-time (-> item :status :transactions :last_successful_update)))
|
||||
:plaid-account/balance (or (some-> a
|
||||
:balances
|
||||
:current
|
||||
double)
|
||||
0.0)}))))
|
||||
(catch Exception e
|
||||
(alog/warn ::couldnt-upsert-account :error e))))
|
||||
|
||||
|
||||
(catch Exception e
|
||||
(alog/warn ::couldnt-upsert-accounts :error e))))
|
||||
|
||||
|
||||
|
||||
|
||||
(defn import-plaid-int []
|
||||
(let [import-batch (t/start-import-batch :import-source/plaid "Automated plaid user")
|
||||
(let [_ (upsert-accounts)
|
||||
import-batch (t/start-import-batch :import-source/plaid "Automated plaid user")
|
||||
end (atime/local-now)
|
||||
start (time/plus end (time/days -30))
|
||||
plaid-merchant->vendor-id (build-plaid-merchant->vendor-id)]
|
||||
|
||||
80
src/clj/auto_ap/import/plaid.fiddle
Normal file
80
src/clj/auto_ap/import/plaid.fiddle
Normal file
@@ -0,0 +1,80 @@
|
||||
(ns auto-ap.import.plaid)
|
||||
|
||||
(let [end (atime/local-now)
|
||||
start (time/plus end (time/days -30))
|
||||
[_ _ external-id access-token] (first (get-plaid-accounts (dc/db conn) "BCFM"))]
|
||||
(p/get-balance access-token))
|
||||
|
||||
(def g *1)
|
||||
|
||||
(take 5 (:transactions g))
|
||||
|
||||
;; => ({:account_id "Dpj0d9yKmXsOxBd0eaL4UONyEJYomNIX7kba3",
|
||||
;; :balances
|
||||
;; {:available nil,
|
||||
;; :current 17764.42,
|
||||
;; :iso_currency_code "USD",
|
||||
;; :limit nil,
|
||||
;; :unofficial_currency_code nil},
|
||||
;; :mask "1006",
|
||||
;; :name "NICHOLAS TAPTELIS -91006",
|
||||
;; :official_name "Business Gold Rewards Card",
|
||||
;; :subtype "credit card",
|
||||
;; :type "credit"})
|
||||
|
||||
|
||||
(dc/q '[:find (pull ?pa [{ :plaid-item/_accounts [*]}])
|
||||
:in $ ?ba
|
||||
:where [?ba :bank-account/plaid-account ?pa]]
|
||||
(dc/db conn)
|
||||
[:bank-account/code "VS-BA6149"])
|
||||
|
||||
(auto-ap.datomic/pull-attr (dc/db conn) :db/id [:bank-account/code "VS-BA6149"])
|
||||
|
||||
(p/get-transactions "access-production-1aee2c7d-0a57-403d-83dc-28a252fb92b4" "jZrAPpjMoLU55oZdpPVVuk8D7XVjXnuv1EJy6" (clj-time.coerce/to-date-time #inst "2024-05-01") (clj-time.coerce/to-date-time #inst "2024-05-15"))
|
||||
|
||||
(user/init-repl)
|
||||
|
||||
|
||||
|
||||
(defn import-plaid-int-2 []
|
||||
(let [
|
||||
import-batch (t/start-import-batch :import-source/plaid "Automated plaid user")
|
||||
end (atime/local-now)
|
||||
start (time/plus end (time/days -30))
|
||||
plaid-merchant->vendor-id (build-plaid-merchant->vendor-id)]
|
||||
(try
|
||||
(doseq [[bank-account-id client-id external-id access-token] (get-plaid-accounts (dc/db conn))
|
||||
:when (= bank-account-id 17592234448533)
|
||||
|
||||
:let [_ (println "TRYING INTEGRATION")
|
||||
transaction-result (wrap-integration #(p/get-transactions access-token external-id start end)
|
||||
bank-account-id)
|
||||
_ (println "FOUND" (count (:transactions transaction-result)))
|
||||
accounts-by-id (by :account_id (:accounts transaction-result))]
|
||||
transaction (:transactions transaction-result)]
|
||||
(when (not (:pending transaction))
|
||||
(t/import-transaction! import-batch (doto (assoc (plaid->transaction (assoc transaction
|
||||
:account
|
||||
(accounts-by-id (:account_id transaction)))
|
||||
plaid-merchant->vendor-id)
|
||||
:transaction/bank-account bank-account-id
|
||||
:transaction/client client-id)
|
||||
(#(println (:transaction/date %)))))))
|
||||
(try
|
||||
(rebuild-search-index)
|
||||
(catch Exception e
|
||||
(alog/error ::cant-index-plaid
|
||||
:error e)
|
||||
(println "CANT INDEX")))
|
||||
(t/finish! import-batch)
|
||||
(println "DONE")
|
||||
(catch Exception e
|
||||
(println "FAIL")
|
||||
(t/fail! import-batch e)))))
|
||||
|
||||
(import-plaid-int-2)
|
||||
|
||||
{:transaction/bank-account 17592234448533, :transaction/date #inst "2024-05-14T07:00:00.000-00:00", :transaction/client 17592234448526, :transaction/status "POSTED", :transaction/plaid-merchant {:plaid-merchant/name "Integreat Restau", :db/id "99cb3ac3-1326-4090-8e36-721a0db3a7cf"}, :db/id "89d4fb46-bb17-436f-b1f9-505bfd67e3ec", :transaction/id "0c56701d74584f800b19b1ce6c7b15212b420626a0d0d28761bab4fec4e10ee8", :transaction/description-original "INTEGREAT RESTAU DES:ACH ID:408-340-3111 INDN:PALA UMBERTO CO ID:XXXXX03620 CCD", :transaction/amount -275.0, :transaction/raw-id "drKydaj39qUPPaR0DQyyHVrD4zb8XBIyxe9QJ"}
|
||||
(auto-ap.datomic/pull-attr (dc/db conn) :db/id [:bank-account/code "NGGG-CB"])
|
||||
|
||||
@@ -85,30 +85,35 @@
|
||||
(alog/info ::searching-unpaid-invoice
|
||||
:client-id client-id
|
||||
:amount amount)
|
||||
(let [candidate-invoices-vendor-groups (->> (dc/q {:find ['?vendor-id '?e '?outstanding-balance '?d]
|
||||
:in ['$ '?client-id]
|
||||
:where ['[?e :invoice/client ?client-id]
|
||||
'[?e :invoice/status :invoice-status/unpaid]
|
||||
'(not [_ :invoice-payment/invoice ?e])
|
||||
'[?e :invoice/vendor ?vendor-id]
|
||||
'[?e :invoice/outstanding-balance ?outstanding-balance]
|
||||
'[?e :invoice/date ?d]]}
|
||||
(dc/db conn) client-id)
|
||||
(sort-by last) ;; sort by scheduled payment date
|
||||
(group-by first) ;; group by vendors
|
||||
vals)
|
||||
considerations (for [candidate-invoices candidate-invoices-vendor-groups
|
||||
invoice-count (range 1 32)
|
||||
consideration (partition invoice-count 1 candidate-invoices)
|
||||
:when (dollars= (reduce (fn [acc [_ _ amount]]
|
||||
(+ acc amount)) 0.0 consideration)
|
||||
(- amount))]
|
||||
consideration)]
|
||||
(alog/info ::unpaid-invoice-considerations-found
|
||||
:client-id client-id
|
||||
:amount amount
|
||||
:count (count considerations))
|
||||
considerations))
|
||||
(try
|
||||
(let [candidate-invoices-vendor-groups (->> (dc/q {:find ['?vendor-id '?e '?outstanding-balance '?d]
|
||||
:in ['$ '?client-id]
|
||||
:where ['[?e :invoice/client ?client-id]
|
||||
'[?e :invoice/status :invoice-status/unpaid]
|
||||
'(not [_ :invoice-payment/invoice ?e])
|
||||
'[?e :invoice/vendor ?vendor-id]
|
||||
'[?e :invoice/outstanding-balance ?outstanding-balance]
|
||||
'[?e :invoice/date ?d]]}
|
||||
(dc/db conn) client-id)
|
||||
(sort-by last) ;; sort by scheduled payment date
|
||||
(group-by first) ;; group by vendors
|
||||
vals)
|
||||
considerations (for [candidate-invoices candidate-invoices-vendor-groups
|
||||
invoice-count (range 1 32)
|
||||
consideration (partition invoice-count 1 candidate-invoices)
|
||||
:when (dollars= (reduce (fn [acc [_ _ amount]]
|
||||
(+ acc amount)) 0.0 consideration)
|
||||
(- amount))]
|
||||
consideration)]
|
||||
(alog/info ::unpaid-invoice-considerations-found
|
||||
:client-id client-id
|
||||
:amount amount
|
||||
:count (count considerations))
|
||||
considerations)
|
||||
(catch Exception e
|
||||
(alog/error ::cant-get-considerations
|
||||
:error e)
|
||||
[])))
|
||||
|
||||
(defn match-transaction-to-single-unfulfilled-autopayments [amount client-id]
|
||||
(let [considerations (match-transaction-to-unfulfilled-autopayments amount client-id)]
|
||||
|
||||
Reference in New Issue
Block a user