revamped logging!

This commit is contained in:
Bryce Covert
2020-08-01 17:07:17 -07:00
parent 49f98522c0
commit 51b097766f
33 changed files with 598 additions and 485 deletions

View File

@@ -4,4 +4,4 @@ RUN apk add poppler
RUN apk add poppler-utils RUN apk add poppler-utils
COPY target/auto-ap.jar /usr/local/ COPY target/auto-ap.jar /usr/local/
COPY config /usr/local/config/ COPY config /usr/local/config/
CMD java -jar /usr/local/auto-ap.jar CMD java -Dlogback.configurationFile=logback-prod.xml -jar /usr/local/auto-ap.jar

View File

@@ -45,6 +45,11 @@
[buddy/buddy-auth "2.1.0"] [buddy/buddy-auth "2.1.0"]
[buddy/buddy-sign "2.1.0"] [buddy/buddy-sign "2.1.0"]
[nrepl "0.6.0" :exclusions [org.clojure/tools.logging]] [nrepl "0.6.0" :exclusions [org.clojure/tools.logging]]
[org.clojure/tools.logging "1.1.0"]
[ch.qos.logback/logback-classic "1.2.3"]
[ch.qos.logback.contrib/logback-jackson "0.1.5"]
[ch.qos.logback.contrib/logback-json-classic "0.1.5"]
[spootnik/unilog "0.7.24"]
[clj-time "0.14.3"] [clj-time "0.14.3"]
[io.forward/clojure-mail "1.0.7"] [io.forward/clojure-mail "1.0.7"]
[ring/ring-json "0.4.0" :exclusions [cheshire]] [ring/ring-json "0.4.0" :exclusions [cheshire]]
@@ -82,6 +87,7 @@
{:resource-paths ["resources" "target"] {:resource-paths ["resources" "target"]
:dependencies [[binaryage/devtools "0.9.4"] :dependencies [[binaryage/devtools "0.9.4"]
#_[refactor-nrepl "2.5.0"]
[com.bhauman/figwheel-main "0.2.3" :exclusions [org.clojure/clojurescript [com.bhauman/figwheel-main "0.2.3" :exclusions [org.clojure/clojurescript
ring/ring-core ring/ring-core
ring/ring-codec ring/ring-codec
@@ -134,7 +140,7 @@
:main auto-ap.server :main auto-ap.server
:aot [auto-ap.server auto-ap.datomic.migrate auto-ap.time clj-time.core clj-time.coerce clj-time.format] :aot [auto-ap.server auto-ap.datomic.migrate auto-ap.time clj-time.core clj-time.coerce clj-time.format clojure.tools.logging.impl]
:uberjar-name "auto-ap.jar" :uberjar-name "auto-ap.jar"

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.contrib.json.classic.JsonLayout">
<jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter"/>
<appendLineSeparator>true</appendLineSeparator>
</layout>
</encoder>
</appender>
<root level="info">
<appender-ref ref="CONSOLE" />
</root>
</configuration>

14
resources/logback.xml Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %logger{36} - %green(%msg) %gray(%mdc) %n</pattern>
</encoder>
</appender>
<logger level="info" name="auto-ap" additivity="false">
<appender-ref ref="CONSOLE" />
</logger>
<root level="error">
<appender-ref ref="CONSOLE" />
</root>
</configuration>

14
resources/logback_.xml Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.contrib.json.classic.JsonLayout">
<jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter"/>
<appendLineSeparator>true</appendLineSeparator>
</layout>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="CONSOLE" />
</root>
</configuration>

View File

@@ -5,30 +5,33 @@
[auto-ap.time :as time] [auto-ap.time :as time]
[clj-time.coerce :as coerce] [clj-time.coerce :as coerce]
[mount.core :as mount] [mount.core :as mount]
[yang.scheduler :as scheduler])) [yang.scheduler :as scheduler]
[unilog.context :as lc]
[clojure.tools.logging :as log]))
(defn close-auto-invoices [] (defn close-auto-invoices []
(try (lc/with-context {:source "close-auto-invoices"}
(try
(let [invoices-to-close (d/query {:query {:find ['?e]
:in ['$ '?today] (let [invoices-to-close (d/query {:query {:find ['?e]
:where ['[?e :invoice/automatically-paid-when-due true] :in ['$ '?today]
'[?e :invoice/status :invoice-status/unpaid] :where ['[?e :invoice/automatically-paid-when-due true]
'[?e :invoice/due ?d] '[?e :invoice/status :invoice-status/unpaid]
'[(<= ?d ?today)]]} '[?e :invoice/due ?d]
:args [(d/db conn) (coerce/to-date (time/local-now))]})] '[(<= ?d ?today)]]}
(println "Closing " (count invoices-to-close) "automatic invoices") :args [(d/db conn) (coerce/to-date (time/local-now))]})]
(some->> invoices-to-close (log/info "Closing " (count invoices-to-close) "automatic invoices")
seq (some->> invoices-to-close
seq
(mapv (fn [[i]] {:db/id i (mapv (fn [[i]] {:db/id i
:invoice/outstanding-balance 0.0 :invoice/outstanding-balance 0.0
:invoice/status :invoice-status/paid})) :invoice/status :invoice-status/paid}))
(d/transact conn) (d/transact conn)
deref) deref)
(println "Closed " (count invoices-to-close) "automatic invoices")) (log/info "Closed " (count invoices-to-close) "automatic invoices"))
(catch Exception e (catch Exception e
(println (.toString e))))) (log/error e)))))
(mount/defstate close-auto-invoices-worker (mount/defstate close-auto-invoices-worker
:start (scheduler/every 60000 close-auto-invoices) :start (scheduler/every 60000 close-auto-invoices)

View File

@@ -156,8 +156,7 @@
[(->> (graphql-results ids-to-retrieve db args)) [(->> (graphql-results ids-to-retrieve db args))
matching-count matching-count
(doto outstanding]))
outstanding println)]))
(defn get-by-id [id] (defn get-by-id [id]
(-> (d/db (d/connect uri)) (-> (d/db (d/connect uri))
@@ -186,8 +185,7 @@
[(not= ?e ?invoice-id)] [(not= ?e ?invoice-id)]
]} ]}
:args [(d/db (d/connect uri)) invoice-number vendor client (or id 0)]} :args [(d/db (d/connect uri)) invoice-number vendor client (or id 0)]}))
true (doto println)))
(map first) (map first)
(map <-datomic))) (map <-datomic)))

View File

@@ -115,8 +115,7 @@
"status" ['[?e :transaction/status ?sort-status]]} "status" ['[?e :transaction/status ?sort-status]]}
args) args)
true true
(merge-query {:query {:find ['?e] :where ['[?e :transaction/id]]}})) (merge-query {:query {:find ['?e] :where ['[?e :transaction/id]]}}))]
_ (println query)]
(cond->> query (cond->> query
true (d/query) true (d/query)
true (apply-sort-3 args) true (apply-sort-3 args)

View File

@@ -41,7 +41,6 @@
first first
first first
(update :user/role :db/ident))] (update :user/role :db/ident))]
(println "USER" user)
(if user (if user
user user
(let [new-user-trans @(d/transact (d/connect uri) [(cond-> new-user (let [new-user-trans @(d/transact (d/connect uri) [(cond-> new-user

View File

@@ -7,6 +7,7 @@
[com.walmartlabs.lacinia.resolve :as resolve] [com.walmartlabs.lacinia.resolve :as resolve]
[buddy.auth :refer [throw-unauthorized]] [buddy.auth :refer [throw-unauthorized]]
[auto-ap.utils :refer [by]] [auto-ap.utils :refer [by]]
[auto-ap.logging :refer [info-event warn-event error-event]]
[auto-ap.graphql.utils :refer [assert-admin can-see-client? assert-can-see-client]] [auto-ap.graphql.utils :refer [assert-admin can-see-client? assert-can-see-client]]
[auto-ap.datomic :refer [uri merge-query]] [auto-ap.datomic :refer [uri merge-query]]
[datomic.api :as d] [datomic.api :as d]
@@ -29,7 +30,10 @@
[auto-ap.graphql.transaction-rules :as gq-transaction-rules] [auto-ap.graphql.transaction-rules :as gq-transaction-rules]
[auto-ap.time :as time] [auto-ap.time :as time]
[clojure.walk :as walk] [clojure.walk :as walk]
[clojure.string :as str]) [clojure.string :as str]
[clojure.tools.logging :as log]
[yang.time :refer [time-it]]
[unilog.context :as lc])
(:import (:import
(clojure.lang IPersistentMap))) (clojure.lang IPersistentMap)))
@@ -945,7 +949,6 @@
(filter (fn [[potential-id potential-date]] (filter (fn [[potential-id potential-date]]
(let [date (coerce/to-date-time date) (let [date (coerce/to-date-time date)
potential-date (coerce/to-date-time potential-date)] potential-date (coerce/to-date-time potential-date)]
#_(println "HERE" id potential-id potential-date date)
(and (= id potential-id) (and (= id potential-id)
(<= (t/in-days (apply t/interval (sort [date potential-date]))) 10))))) (<= (t/in-days (apply t/interval (sort [date potential-date]))) 10)))))
conj conj
@@ -1010,7 +1013,6 @@
:amount amount :amount amount
:date (coerce/to-date-time next)}) :date (coerce/to-date-time next)})
is-week-a? (fn [d] is-week-a? (fn [d]
(println d)
(= 0 (mod (t/in-weeks (t/interval first-week-a d)) 2)))] (= 0 (mod (t/in-weeks (t/interval first-week-a d)) 2)))]
{:beginning_balance total-cash {:beginning_balance total-cash
@@ -1110,20 +1112,24 @@
([id q] ([id q]
(query id q nil )) (query id q nil ))
([id q v] ([id q v]
(println "executing graphql query" id q v)
(try (lc/with-context {:query q}
(let [result (time (simplify (execute schema q v {:id id})))] (log/info "Executing query" q)
(when (seq (:errors result))
(throw (ex-info "GraphQL error" {:result result}))) (try
result) (let [[result time] (time-it (simplify (execute schema q v {:id id})))]
(info-event "Query completed"
{:time (:time time)
:errors (seq (:errors result))})
(when (seq (:errors result))
(throw (ex-info "GraphQL error" {:result result})))
result)
(catch Exception e (catch Exception e
(if-let [v (:validation-error (ex-data e))] (if-let [v (:validation-error (ex-data e))]
(println "validation error" v) (warn-event "validation error" {:validation-error v
(do :data (ex-data e)})
(.printStackTrace e) (error-event "query error" {:error e}))
(println e )))
(throw e))))) (throw e))))))
#_(query nil "{ ledger_page { count }}" nil)

View File

@@ -19,8 +19,6 @@
(let [client (when (:id edit_client) (d-clients/get-by-id (:id edit_client))) (let [client (when (:id edit_client) (d-clients/get-by-id (:id edit_client)))
id (or (:db/id client) "new-client") id (or (:db/id client) "new-client")
_ (println id)
_ (println edit_client)
_ (when client _ (when client
@(d/transact (d/connect uri) @(d/transact (d/connect uri)
(into (into
@@ -79,7 +77,6 @@
) )
(:forecasted_transactions edit_client))]] (:forecasted_transactions edit_client))]]
result @(d/transact (d/connect uri) transactions)] result @(d/transact (d/connect uri) transactions)]
(println result "ID" id)
(-> result :tempids (get id) (or id) d-clients/get-by-id (-> result :tempids (get id) (or id) d-clients/get-by-id
(update :client/location-matches (update :client/location-matches
(fn [lms] (fn [lms]

View File

@@ -58,7 +58,6 @@
:location location})) :location location}))
(defn add-invoice-transaction [{:keys [total invoice_number location automatically_paid_when_due client_id vendor_id vendor_name date due expense_accounts] :as in}] (defn add-invoice-transaction [{:keys [total invoice_number location automatically_paid_when_due client_id vendor_id vendor_name date due expense_accounts] :as in}]
(println date)
(let [vendor (d-vendors/get-by-id vendor_id) (let [vendor (d-vendors/get-by-id vendor_id)
account (:vendor/default-account vendor) account (:vendor/default-account vendor)
_ (when-not (:db/id account) _ (when-not (:db/id account)
@@ -119,11 +118,10 @@
(defn edit-invoice [context {{:keys [id due invoice_number total vendor_id date client_id expense_accounts automatically_paid_when_due] :as in} :invoice} value] (defn edit-invoice [context {{:keys [id due invoice_number total vendor_id date client_id expense_accounts automatically_paid_when_due] :as in} :invoice} value]
(let [invoice (d-invoices/get-by-id id) (let [invoice (d-invoices/get-by-id id)
_ (when (seq (doto (d-invoices/find-conflicting {:db/id id _ (when (seq (d-invoices/find-conflicting {:db/id id
:invoice/invoice-number invoice_number :invoice/invoice-number invoice_number
:invoice/vendor (:db/id (:invoice/vendor invoice)) :invoice/vendor (:db/id (:invoice/vendor invoice))
:invoice/client (:db/id (:invoice/client invoice))}) :invoice/client (:db/id (:invoice/client invoice))}))
println))
(throw (ex-info (str "Invoice '" invoice_number "' already exists.") {:invoice-number invoice_number}))) (throw (ex-info (str "Invoice '" invoice_number "' already exists.") {:invoice-number invoice_number})))
expense-account-total (reduce + 0 (map (fn [x] (Double/parseDouble (:amount x))) expense_accounts)) expense-account-total (reduce + 0 (map (fn [x] (Double/parseDouble (:amount x))) expense_accounts))
@@ -151,7 +149,6 @@
(defn void-invoice [context {id :invoice_id} value] (defn void-invoice [context {id :invoice_id} value]
(let [invoice (d-invoices/get-by-id id) (let [invoice (d-invoices/get-by-id id)
_ (println invoice)
_ (assert-can-see-client (:id context) (:db/id (:invoice/client invoice))) _ (assert-can-see-client (:id context) (:db/id (:invoice/client invoice)))
updated-invoice (d-invoices/update {:db/id id updated-invoice (d-invoices/update {:db/id id
:invoice/total 0.0 :invoice/total 0.0

View File

@@ -12,7 +12,9 @@
[auto-ap.parse.util :as parse] [auto-ap.parse.util :as parse]
[datomic.api :as d] [datomic.api :as d]
[auto-ap.parse.templates :as t] [auto-ap.parse.templates :as t]
[auto-ap.datomic.clients :as d-clients])) [auto-ap.datomic.clients :as d-clients]
[clojure.tools.logging :as log]
[unilog.context :as lc]))
(defn get-ledger-page [context args value] (defn get-ledger-page [context args value]
(let [args (assoc args :id (:id context)) (let [args (assoc args :id (:id context))
@@ -106,15 +108,12 @@
vals vals
(mapcat (fn [a] (mapcat (fn [a]
(map (fn [o] (map (fn [o]
#_(println "override" (:db/id a) (:db/id (:account-client-override/client o)))
#_(print [[(:db/id a) (:db/id (:account-client-override/client o))]])
[[(:db/id a) (:db/id (:account-client-override/client o))] [[(:db/id a) (:db/id (:account-client-override/client o))]
(:account-client-override/name o)]) (:account-client-override/name o)])
(:account/client-overrides a)) (:account/client-overrides a))
) ) ) )
(into {} ))] (into {} ))]
(fn [a] (fn [a]
#_(println a client-id (keys overrides-by-client))
{:name (or (:bank-account/name (bank-accounts a)) {:name (or (:bank-account/name (bank-accounts a))
(overrides-by-client [a client-id]) (overrides-by-client [a client-id])
(:account/name (accounts a))) (:account/name (accounts a)))
@@ -158,7 +157,6 @@
(defn get-profit-and-loss [context args value] (defn get-profit-and-loss [context args value]
(let [client-id (:client_id args) (let [client-id (:client_id args)
_ (assert-can-see-client (:id context) client-id) _ (assert-can-see-client (:id context) client-id)
_ (println args)
all-ledger-entries (full-ledger-for-client client-id) all-ledger-entries (full-ledger-for-client client-id)
lookup-account (build-account-lookup client-id)] lookup-account (build-account-lookup client-id)]
@@ -179,108 +177,113 @@
(defn import-ledger [context args value] (defn import-ledger [context args value]
(assert-admin (:id context)) (assert-admin (:id context))
(let [all-vendors (by :vendor/name (d-vendors/get-graphql {})) (lc/with-context {:area "import ledger"}
all-clients (by :client/code (d-clients/get-all )) (let [all-vendors (by :vendor/name (d-vendors/get-graphql {}))
all-client-bank-accounts (reduce all-clients (by :client/code (d-clients/get-all ))
(fn [acc client] all-client-bank-accounts (reduce
(assoc acc (:client/code client) (fn [acc client]
(set (->> (:client/bank-accounts client) (assoc acc (:client/code client)
(map :bank-account/code) (set (->> (:client/bank-accounts client)
)))) (map :bank-account/code)
{} ))))
(d-clients/get-all)) {}
all-client-locations (reduce (d-clients/get-all))
(fn [acc client] all-client-locations (reduce
(assoc acc (:client/code client) (fn [acc client]
(-> (set (:client/locations client)) (assoc acc (:client/code client)
(conj "HQ") (-> (set (:client/locations client))
(conj "A")))) (conj "HQ")
(conj "A"))))
{}
(d-clients/get-all))
new-hidden-vendors (reduce
(fn [new-vendors {:keys [vendor_name line_items]}]
(if (or (all-vendors vendor_name)
(new-vendors vendor_name))
new-vendors
(assoc new-vendors vendor_name
{:vendor/name vendor_name
:vendor/hidden true
:db/id vendor_name})))
{} {}
(d-clients/get-all)) (:entries args))
new-hidden-vendors (reduce all-vendors (into all-vendors new-hidden-vendors)
(fn [new-vendors {:keys [vendor_name line_items]}] all-accounts (transduce (map (comp str :account/numeric-code)) conj #{} (a/get-accounts))
(if (or (all-vendors vendor_name) transaction (doall (map
(new-vendors vendor_name)) (assoc-error (fn [entry]
new-vendors (let [entry (-> entry
(assoc new-vendors vendor_name (update :amount #(Double/parseDouble %))
{:vendor/name vendor_name (update :line_items
:vendor/hidden true (fn [lis]
:db/id vendor_name}))) (mapv
{} (fn [li ]
(:entries args)) (-> li
all-vendors (into all-vendors new-hidden-vendors) (update :debit #(Double/parseDouble (if (str/blank? %) "0" %)))
all-accounts (transduce (map (comp str :account/numeric-code)) conj #{} (a/get-accounts)) (update :credit #(Double/parseDouble (if (str/blank? %) "0" %)))))
transaction (doall (map lis))))]
(assoc-error (fn [entry] (let [vendor (all-vendors (:vendor_name entry))]
(let [entry (-> entry (when-not (all-clients (:client_code entry))
(update :amount #(Double/parseDouble %)) (throw (Exception. (str "Client '" (:client_code entry )"' not found.")) ))
(update :line_items (when-not vendor
(fn [lis] (throw (Exception. (str "Vendor '" (:vendor_name entry) "' not found."))))
(mapv (when-not (re-find #"\d{1,2}/\d{1,2}/\d{4}" (:date entry))
(fn [li ] (throw (Exception. (str "Date must be MM/dd/yyyy"))))
(-> li (when-not (dollars= (doto (reduce + 0.0 (map :debit (:line_items entry))))
(update :debit #(Double/parseDouble (if (str/blank? %) "0" %))) (reduce + 0.0 (map :credit (:line_items entry))))
(update :credit #(Double/parseDouble (if (str/blank? %) "0" %))))) (throw (Exception. (str "Debits '"
lis))))] (reduce + 0 (map :debit (:line_items entry)))
(let [vendor (all-vendors (:vendor_name entry))] "' and credits '"
(when-not (all-clients (:client_code entry)) (reduce + 0 (map :credit (:line_items entry)))
(throw (Exception. (str "Client '" (:client_code entry )"' not found.")) )) "' do not add up."))))
(when-not vendor (remove-nils
(throw (Exception. (str "Vendor '" (:vendor_name entry) "' not found.")))) {:journal-entry/source (:source entry)
(when-not (re-find #"\d{1,2}/\d{1,2}/\d{4}" (:date entry)) :journal-entry/client [:client/code (:client_code entry)]
(throw (Exception. (str "Date must be MM/dd/yyyy")))) :journal-entry/date (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry)))
(when-not (dollars= (doto (reduce + 0.0 (map :debit (:line_items entry)))) :journal-entry/external-id (:external_id entry)
(reduce + 0.0 (map :credit (:line_items entry)))) :journal-entry/vendor (all-vendors (:vendor_name entry))
(throw (Exception. (str "Debits '" :journal-entry/amount (:amount entry)
(reduce + 0 (map :debit (:line_items entry))) :journal-entry/note (:note entry)
"' and credits '" :journal-entry/cleared-against (:cleared_against entry)
(reduce + 0 (map :credit (:line_items entry)))
"' do not add up."))))
(remove-nils
{:journal-entry/source (:source entry)
:journal-entry/client [:client/code (:client_code entry)]
:journal-entry/date (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry)))
:journal-entry/external-id (:external_id entry)
:journal-entry/vendor (all-vendors (:vendor_name entry))
:journal-entry/amount (:amount entry)
:journal-entry/note (:note entry)
:journal-entry/cleared-against (:cleared_against entry)
:journal-entry/line-items :journal-entry/line-items
(mapv (fn [ea] (mapv (fn [ea]
(when-not (get (when-not (get
(get all-client-locations (:client_code entry)) (get all-client-locations (:client_code entry))
(:location ea)) (:location ea))
(throw (Exception. (str "Location '" (:location ea) "' not found.")))) (throw (Exception. (str "Location '" (:location ea) "' not found."))))
(when (< (or (:debit ea) (:credit ea)) 0.0) (when (< (or (:debit ea) (:credit ea)) 0.0)
(throw (Exception. (str (or (:debit ea) (:credit ea)) "must be greater than 0.")))) (throw (Exception. (str (or (:debit ea) (:credit ea)) "must be greater than 0."))))
(when (and (not (all-accounts (:account_identifier ea))) (when (and (not (all-accounts (:account_identifier ea)))
(not (get (not (get
(get all-client-bank-accounts (:client_code entry)) (get all-client-bank-accounts (:client_code entry))
(:account_identifier ea)))) (:account_identifier ea))))
(throw (Exception. (str "Account '" (:account_identifier ea) "' not found.")))) (throw (Exception. (str "Account '" (:account_identifier ea) "' not found."))))
(remove-nils {:journal-entry-line/account (remove-nils {:journal-entry-line/account
(if (re-matches #"^[0-9]+$" (:account_identifier ea)) (if (re-matches #"^[0-9]+$" (:account_identifier ea))
(:db/id (a/get-account-by-numeric-code-and-sets (Integer/parseInt (:account_identifier ea)) ["default"])) (:db/id (a/get-account-by-numeric-code-and-sets (Integer/parseInt (:account_identifier ea)) ["default"]))
[:bank-account/code (:account_identifier ea)]) [:bank-account/code (:account_identifier ea)])
:journal-entry-line/location (:location ea) :journal-entry-line/location (:location ea)
:journal-entry-line/debit (when (> (:debit ea) 0) :journal-entry-line/debit (when (> (:debit ea) 0)
(:debit ea)) (:debit ea))
:journal-entry-line/credit (when (> (:credit ea) 0) :journal-entry-line/credit (when (> (:credit ea) 0)
(:credit ea))})) (:credit ea))}))
(:line_items entry)) (:line_items entry))
:journal-entry/cleared true}))))) :journal-entry/cleared true})))))
(:entries args))) (:entries args)))
errors (filter :error transaction) errors (filter :error transaction)
success (filter (comp not :error) transaction) success (filter (comp not :error) transaction)
retraction (mapv (fn [x] [:db/retractEntity [:journal-entry/external-id (:journal-entry/external-id x)]]) retraction (mapv (fn [x] [:db/retractEntity [:journal-entry/external-id (:journal-entry/external-id x)]])
success)] success)]
(println (take 4 success)) (log/info "manual ledger import has " (count success) " new rows")
(run! (fn [batch] (println "transacting retraction batch") @(d/transact (d/connect uri) batch)) (partition-all 100 retraction)) (run! (fn [batch]
(run! (fn [batch] (println "transacting success batch") @(d/transact (d/connect uri) batch)) (partition-all 100 success)) (log/info "transacting retraction batch")
{:successful (map (fn [x] {:external_id (:journal-entry/external-id x)}) success) @(d/transact (d/connect uri) batch)) (partition-all 100 retraction))
:existing [] (run! (fn [batch]
:errors (map (fn [x] {:external_id (:external_id x) (log/info "transacting success batch")
:error (:error x)}) errors)})) @(d/transact (d/connect uri) batch)) (partition-all 100 success))
{:successful (map (fn [x] {:external_id (:journal-entry/external-id x)}) success)
:existing []
:errors (map (fn [x] {:external_id (:external_id x)
:error (:error x)}) errors)})))

View File

@@ -18,10 +18,6 @@
[auto-ap.datomic.transaction-rules :as tr] [auto-ap.datomic.transaction-rules :as tr]
[auto-ap.rule-matching :as rm] [auto-ap.rule-matching :as rm]
[clj-time.coerce :as coerce])) [clj-time.coerce :as coerce]))
(defn prn-each [xs]
(doseq [x xs]
(println x))
xs)
(def approval-status->graphql (ident->enum-f :transaction/approval-status)) (def approval-status->graphql (ident->enum-f :transaction/approval-status))

View File

@@ -10,7 +10,6 @@
":user" :user-role/user}) ":user" :user-role/user})
(defn edit-user [context {:keys [edit_user] :as args} value] (defn edit-user [context {:keys [edit_user] :as args} value]
(println args edit_user)
(assert-admin (:id context)) (assert-admin (:id context))
(let [user (d-users/get-by-id (:id edit_user)) (let [user (d-users/get-by-id (:id edit_user))
new-clients (set (map #(Long/parseLong %) (:clients edit_user))) new-clients (set (map #(Long/parseLong %) (:clients edit_user)))
@@ -21,11 +20,10 @@
@(d/transact (d/connect uri) @(d/transact (d/connect uri)
(doto (-> [{:db/id (:db/id user) (-> [{:db/id (:db/id user)
:user/role (role->datomic-role (:role edit_user)) :user/role (role->datomic-role (:role edit_user))
:user/clients new-clients}] :user/clients new-clients}]
(into (map (fn [c] [:db/retract (:db/id user) :user/clients c]) clients-to-remove))) (into (map (fn [c] [:db/retract (:db/id user) :user/clients c]) clients-to-remove))))
clojure.pprint/pprint))
(->graphql (->graphql
(d-users/get-by-id (:id edit_user))))) (d-users/get-by-id (:id edit_user)))))

View File

@@ -1,7 +1,8 @@
(ns auto-ap.graphql.utils (ns auto-ap.graphql.utils
(:require [clojure.string :as str] (:require [clojure.string :as str]
[buddy.auth :refer [throw-unauthorized]] [buddy.auth :refer [throw-unauthorized]]
[clojure.walk :as walk])) [clojure.walk :as walk]
[clojure.tools.logging :as log]))
(defn snake->kebab [s] (defn snake->kebab [s]
@@ -43,24 +44,23 @@
m)) m))
(defn is-admin? [id] (defn is-admin? [id]
(println "role" id)
(= "admin" (:user/role id))) (= "admin" (:user/role id)))
(defn assert-admin [id] (defn assert-admin [id]
(println "role" id)
(when-not (= "admin" (:user/role id)) (when-not (= "admin" (:user/role id))
(log/warn "user " id " not an admin!")
(throw-unauthorized))) (throw-unauthorized)))
(defn can-see-client? [identity client] (defn can-see-client? [identity client]
(when (not client) (when (not client)
(println "WARNING - permission checking for null client")) (log/warn "WARNING - permission checking for null client"))
(or (= "admin" (:user/role identity)) (or (= "admin" (:user/role identity))
((set (map :db/id (:user/clients identity))) (:db/id client)) ((set (map :db/id (:user/clients identity))) (:db/id client))
((set (map :db/id (:user/clients identity))) client))) ((set (map :db/id (:user/clients identity))) client)))
(defn assert-can-see-client [identity client] (defn assert-can-see-client [identity client]
(when-not (can-see-client? identity client) (when-not (can-see-client? identity client)
(println "IDENTITY " identity " can not see company " client) (log/warn "IDENTITY " identity " can not see company " client)
(throw-unauthorized))) (throw-unauthorized)))
(defn limited-clients [id] (defn limited-clients [id]

View File

@@ -72,19 +72,15 @@
(is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/account-overrides account-overrides]) (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/account-overrides account-overrides])
(is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/terms-overrides terms-overrides]) (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/terms-overrides terms-overrides])
(is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/automatically-paid-when-due (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/automatically-paid-when-due
(doto (mapv (mapv
(fn [apwd] (fn [apwd]
{:db/id apwd}) {:db/id apwd})
(:automatically_paid_when_due in)) (:automatically_paid_when_due in))]))
println)]))
_ (println transaction)
transaction-result @(d/transact (d/connect uri) transaction)] transaction-result @(d/transact (d/connect uri) transaction)]
(-> (d-vendors/get-by-id (or (-> transaction-result :tempids (get "vendor")) (-> (d-vendors/get-by-id (or (-> transaction-result :tempids (get "vendor"))
id)) id))
(doto println)
(->graphql)))) (->graphql))))
(defn merge-vendors [context {:keys [from to]} value] (defn merge-vendors [context {:keys [from to]} value]

View File

@@ -16,6 +16,8 @@
[compojure.core :refer :all] [compojure.core :refer :all]
[compojure.route :as route] [compojure.route :as route]
[config.core :refer [env]] [config.core :refer [env]]
[clojure.tools.logging :as log]
[unilog.context :as lc]
[ring.middleware.edn :refer [wrap-edn-params]] [ring.middleware.edn :refer [wrap-edn-params]]
[ring.middleware.multipart-params :as mp] [ring.middleware.multipart-params :as mp]
[ring.middleware.params :refer [wrap-params]] [ring.middleware.params :refer [wrap-params]]
@@ -55,10 +57,20 @@
static-routes)) static-routes))
(defn wrap-logging [handler]
(fn [request]
(lc/with-context {:uri (:uri request)
:source "request"
:user-role (:user/role (:identity request))
:user-name (:user/name (:identity request))}
(log/info "Beginning request" (:uri request))
(handler request))))
(def app (def app
(-> #'app-routes (-> #'app-routes
(wrap-logging)
(wrap-authorization auth-backend) (wrap-authorization auth-backend)
(wrap-authentication auth-backend) (wrap-authentication auth-backend)
(wrap-reload) (wrap-reload)

View File

@@ -4,7 +4,10 @@
[mount.core :as mount] [mount.core :as mount]
[auto-ap.datomic.accounts :as a] [auto-ap.datomic.accounts :as a]
[auto-ap.datomic :refer [uri remove-nils]] [auto-ap.datomic :refer [uri remove-nils]]
[clojure.spec.alpha :as s])) [clojure.spec.alpha :as s]
[clojure.tools.logging :as log]
[auto-ap.logging :refer [info-event]]
[unilog.context :as lc]))
(defn datums->impacted-entity [db [e changes]] (defn datums->impacted-entity [db [e changes]]
@@ -129,66 +132,75 @@
(defn process-one [] (defn process-one []
(let [transaction (.take tx-report-queue) (lc/with-context {:source "process-txes"}
_ (println "processing transaction") (try
db (:db-after transaction) (let [transaction (.take tx-report-queue)
affected-entities (->> (:tx-data transaction) _ (log/info "Converting tranasction to ledger")
(map (fn [^datomic.db.Datum x] db (:db-after transaction)
{:e (:e x) affected-entities (->> (:tx-data transaction)
:a (d/ident db (:a x)) (map (fn [^datomic.db.Datum x]
:v (:v x) {:e (:e x)
:added (:added x)})) :a (d/ident db (:a x))
(group-by :e) :v (:v x)
(mapcat #(datums->impacted-entity db %)) :added (:added x)}))
(set)) (group-by :e)
_ (println "processing transaction affected" (count affected-entities)) (mapcat #(datums->impacted-entity db %))
d-txs (->> affected-entities (set))
(map #(entity-change->ledger db %)) _ (info-event (str "Found " (count affected-entities) " affected entities")
(filter seq)) {:affected-entities (count affected-entities)})
retractions (map (fn [[_ e]] [:db/retractEntity [:journal-entry/original-entity e]]) affected-entities)] d-txs (->> affected-entities
(map #(entity-change->ledger db %))
(filter seq))
retractions (map (fn [[_ e]] [:db/retractEntity [:journal-entry/original-entity e]]) affected-entities)]
(when (seq retractions) (when (seq retractions)
@(d/transact conn retractions)) @(d/transact conn retractions))
(doseq [d-tx d-txs] (doseq [d-tx d-txs]
@(d/transact conn [d-tx])))) @(d/transact conn [d-tx]))
(log/info "Succesfully process transaction"))
(catch Exception e
(log/error e)))))
(mount/defstate process-txes-worker (mount/defstate process-txes-worker
:start (scheduler/run-fun process-one 1) :start (scheduler/run-fun process-one 1)
:stop (-> process-txes-worker :running? (reset! false))) :stop (-> process-txes-worker :running? (reset! false)))
(defn reconcile-ledger [] (defn reconcile-ledger []
(try (lc/with-context {:source "reconcile-ledger"}
(println "Attempting to reconcile the ledger") (try
(let [txes-missing-ledger-entries (->> (d/query {:query {:find ['?t ] (log/info "Attempting to reconcile the ledger")
:in ['$] (let [txes-missing-ledger-entries (->> (d/query {:query {:find ['?t ]
:where ['[?t :transaction/date] :in ['$]
'(not [?t :transaction/approval-status :transaction-approval-status/excluded]) :where ['[?t :transaction/date]
'(not-join [?t] [?e :journal-entry/original-entity ?t])]} '(not [?t :transaction/approval-status :transaction-approval-status/excluded])
:args [(d/db conn)]}) '(not-join [?t] [?e :journal-entry/original-entity ?t])]}
(map first) :args [(d/db conn)]})
(mapv #(entity-change->ledger (d/db conn) [:transaction %]))) (map first)
(mapv #(entity-change->ledger (d/db conn) [:transaction %])))
invoices-missing-ledger-entries (->> (d/query {:query {:find ['?t ]
:in ['$]
:where ['[?t :invoice/date]
'(not [?t :invoice/status :invoice-status/voided])
'(not [?t :invoice/import-status :import-status/pending])
'(not [?t :invoice/exclude-from-ledger true])
'(not-join [?t] [?e :journal-entry/original-entity ?t])]}
:args [(d/db conn)]})
(map first)
(mapv #(entity-change->ledger (d/db conn) [:invoice %])))
repairs (vec (concat txes-missing-ledger-entries invoices-missing-ledger-entries))]
invoices-missing-ledger-entries (->> (d/query {:query {:find ['?t ]
(when (seq repairs) :in ['$]
(println "repairing " (count txes-missing-ledger-entries) " missing transactions, " (count invoices-missing-ledger-entries) " missing invoices that were missing ledger entries") :where ['[?t :invoice/date]
'(not [?t :invoice/status :invoice-status/voided])
'(not [?t :invoice/import-status :import-status/pending])
'(not [?t :invoice/exclude-from-ledger true])
'(not-join [?t] [?e :journal-entry/original-entity ?t])]}
:args [(d/db conn)]})
(map first)
(mapv #(entity-change->ledger (d/db conn) [:invoice %])))
repairs (vec (concat txes-missing-ledger-entries invoices-missing-ledger-entries))]
@(d/transact conn repairs))) (when (seq repairs)
(catch Exception e (log/warn "repairing " (count txes-missing-ledger-entries) " missing transactions, " (count invoices-missing-ledger-entries) " missing invoices that were missing ledger entries")
(println e))))
@(d/transact conn repairs))
(log/info "Finished reconciling ledger"))
(catch Exception e
(log/error e)))))
(mount/defstate reconciliation-frequency :start 60000) (mount/defstate reconciliation-frequency :start 60000)

View File

@@ -0,0 +1,15 @@
(ns auto-ap.logging
(:require [clojure.tools.logging :as log]
[unilog.context :as lc]))
(defn info-event [message context]
(lc/with-context context
(log/info message)))
(defn warn-event [message context]
(lc/with-context context
(log/warn message)))
(defn error-event [message context]
(lc/with-context context
(log/warn message)))

View File

@@ -1,15 +1,16 @@
(ns auto-ap.parse (ns auto-ap.parse
(:require [auto-ap.parse.excel :as excel] (:require [auto-ap.parse.csv :as csv]
[auto-ap.parse.excel :as excel]
[auto-ap.parse.templates :as t] [auto-ap.parse.templates :as t]
[auto-ap.parse.util :as u] [auto-ap.parse.util :as u]
[auto-ap.parse.csv :as csv]
[clj-fuzzy.metrics :as m] [clj-fuzzy.metrics :as m]
[clojure.java.shell :as sh]
[clojure.string :as str]
[clj-time.format :as f]
[clj-time.core :as time] [clj-time.core :as time]
[clojure.set :as set])) [clj-time.format :as f]
[clojure.java.shell :as sh]
[clojure.set :as set]
[clojure.string :as str]
[clojure.tools.logging :as log]
[auto-ap.logging :refer [info-event]]))
(def last-text (atom nil)) (def last-text (atom nil))
@@ -19,7 +20,8 @@
(defn extract-template (defn extract-template
([text template] ([text template]
(println "template" template) (log/info "Template was determined to be" template)
(if (:multi template) (if (:multi template)
(mapcat (mapcat
#(extract-template % text (dissoc template :multi)) #(extract-template % text (dissoc template :multi))
@@ -39,16 +41,15 @@
str/trim ) str/trim )
[value-parser parser-params] (-> template :parser k)] [value-parser parser-params] (-> template :parser k)]
(assoc result k (try (assoc result k (try
(println "applying parser" value-parser "to value" value)
(u/parse-value value-parser parser-params value) (u/parse-value value-parser parser-params value)
(catch Exception e (catch Exception e
(println e)))))) (log/warn e))))))
{:vendor-code (:vendor template) {:vendor-code (:vendor template)
:text text :text text
:full-text full-text}))]))) :full-text full-text}))])))
(defn parse [text] (defn parse [text]
(println "Parsing PDF " text) (info-event "Parsing pdf text" {:raw text})
(reset! last-text text) (reset! last-text text)
(->> t/pdf-templates (->> t/pdf-templates
(filter (partial template-applies? text)) (filter (partial template-applies? text))

View File

@@ -2,29 +2,29 @@
(:require [auto-ap.parse.util :as u] (:require [auto-ap.parse.util :as u]
[clojure.data.csv :as csv] [clojure.data.csv :as csv]
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.string :as str])) [clojure.string :as str]
[clojure.tools.logging :as log]))
(defn determine (defn determine
[[header :as z]] [[header :as z]]
(prn header) (let [csv-type (cond (str/includes? (second header) "Customer's PO No.")
(doto (cond (str/includes? (second header) "Customer's PO No.") :mama-lus
:mama-lus
(str/includes? (str header) "Ship-To Number") (str/includes? (str header) "Ship-To Number")
:sysco-style-2 :sysco-style-2
(str/includes? (str header) "Closed Date") (str/includes? (str header) "Closed Date")
:sysco-style-1 :sysco-style-1
(str/includes? (str header) "Business Unit") (str/includes? (str header) "Business Unit")
:mission :mission
(str/includes? (str header) "Document Number") (str/includes? (str header) "Document Number")
:philz :philz
:else :else
nil) nil)]
println)) (log/info "csv type was determined to be" csv-type)))
(defmulti parse-csv (defmulti parse-csv
determine determine
@@ -44,7 +44,6 @@
(defmethod parse-csv :sysco-style-1 (defmethod parse-csv :sysco-style-1
[rows] [rows]
(println "Importing Sysco-styled 1")
(let [header (first rows)] (let [header (first rows)]
(transduce (transduce
(comp (drop 1) (comp (drop 1)
@@ -68,7 +67,7 @@
(defmethod parse-csv :sysco-style-2 (defmethod parse-csv :sysco-style-2
[rows] [rows]
(println "Importing Sysco-styled 1")
(let [header (first rows)] (let [header (first rows)]
(transduce (transduce
(comp (drop 1) (comp (drop 1)
@@ -125,9 +124,7 @@
(comp (comp
(filter (fn [[dt _ doc-number name _ status _ _ amount :as row]] (filter (fn [[dt _ doc-number name _ status _ _ amount :as row]]
(= status "Billed"))) (= status "Billed")))
(map (fn [[dt _ doc-number name _ _ _ _ amount :as row]] (map (fn [[dt _ doc-number name _ _ _ _ amount :as row]]
(print name)
{:vendor-code "PHILZ COFFEE, INC" {:vendor-code "PHILZ COFFEE, INC"
:customer-identifier name :customer-identifier name
:invoice-number doc-number :invoice-number doc-number

View File

@@ -3,7 +3,8 @@
[clojure.java.shell :as sh] [clojure.java.shell :as sh]
[clojure.string :as str] [clojure.string :as str]
[clj-time.format :as f] [clj-time.format :as f]
[clj-time.core :as time])) [clj-time.core :as time]
[clojure.tools.logging :as log]))
(defmulti parse-value (fn [method _ _] (defmulti parse-value (fn [method _ _]
method)) method))
@@ -39,7 +40,7 @@
(reduced (time/from-time-zone (f/parse (f/formatter format) value) (reduced (time/from-time-zone (f/parse (f/formatter format) value)
(time/time-zone-for-id "America/Los_Angeles"))) (time/time-zone-for-id "America/Los_Angeles")))
(catch Exception e (catch Exception e
(println (.getMessage e)) (log/warn e)
nil))) nil)))
nil nil
format) format)

View File

@@ -4,7 +4,8 @@
[clj-http.client :as http] [clj-http.client :as http]
[clj-time.core :as time] [clj-time.core :as time]
[compojure.core :refer [GET defroutes]] [compojure.core :refer [GET defroutes]]
[config.core :refer [env]])) [config.core :refer [env]]
[clojure.tools.logging :as log]))
(def google-client-id "264081895820-0nndcfo3pbtqf30sro82vgq5r27h8736.apps.googleusercontent.com") (def google-client-id "264081895820-0nndcfo3pbtqf30sro82vgq5r27h8736.apps.googleusercontent.com")
(def google-client-secret "OC-WemHurPXYpuIw5cT-B90g") (def google-client-secret "OC-WemHurPXYpuIw5cT-B90g")
@@ -19,7 +20,6 @@
(defroutes routes (defroutes routes
(GET "/oauth" {{:strs [code]} :query-params :keys [scheme] :as r {:strs [host]} :headers} (GET "/oauth" {{:strs [code]} :query-params :keys [scheme] :as r {:strs [host]} :headers}
(println "Authenticating with" r "..." code)
(try (try
(let [auth (-> "https://accounts.google.com/o/oauth2/token" (let [auth (-> "https://accounts.google.com/o/oauth2/token"
(http/post (http/post
@@ -30,37 +30,34 @@
"grant_type" "authorization_code"} "grant_type" "authorization_code"}
:as :json}) :as :json})
:body) :body)
_ (println auth)
token (:access_token auth) token (:access_token auth)
profile (-> (http/get "https://www.googleapis.com/oauth2/v1/userinfo" profile (-> (http/get "https://www.googleapis.com/oauth2/v1/userinfo"
{:headers {"Authorization" (str "Bearer " token)} :as :json}) {:headers {"Authorization" (str "Bearer " token)} :as :json})
:body :body)
(doto println))
user (users/find-or-insert! {:user/provider "google" user (users/find-or-insert! {:user/provider "google"
:user/provider-id (:id profile) :user/provider-id (:id profile)
:user/role :user-role/none :user/role :user-role/none
:user/name (:name profile)}) :user/name (:name profile)})
] ]
(println "authenticated as user" user) (log/info "authenticated as user" user)
;; TODO - these namespaces are not being transmitted/deserialized properly ;; TODO - these namespaces are not being transmitted/deserialized properly
(if (and token user) (if (and token user)
(let [jwt (jwt/sign (doto {:user (:name profile) (let [jwt (jwt/sign {:user (:name profile)
:exp (time/plus (time/now) (time/days 30)) :exp (time/plus (time/now) (time/days 30))
:user/clients (map (fn [c] :user/clients (map (fn [c]
(dissoc c :client/bank-accounts :client/location-matches :client/forecasted-transactions :client/matches :client/week-a-debits :client/week-a-credits :client/week-b-debits :client/week-b-credits :client/signature-file :client/address)) (dissoc c :client/bank-accounts :client/location-matches :client/forecasted-transactions :client/matches :client/week-a-debits :client/week-a-credits :client/week-b-debits :client/week-b-credits :client/signature-file :client/address))
(:user/clients user)) (:user/clients user))
:user/role (name (:user/role user)) :user/role (name (:user/role user))
:user/name (:name profile)} :user/name (:name profile)}
println)
(:jwt-secret env) (:jwt-secret env)
{:alg :hs512})] {:alg :hs512})]
(println "authenticated. using jwt" jwt)
{:status 301 {:status 301
:headers {"Location" (str "/?jwt=" jwt)}}) :headers {"Location" (str "/?jwt=" jwt)}})
{:status 401 {:status 401
:body "Couldn't authenticate"})) :body "Couldn't authenticate"}))
(catch Exception e (catch Exception e
(log/warn e )
{:status 401 {:status 401
:body (str "Couldn't authenticate " (.toString e))})))) :body (str "Couldn't authenticate " (.toString e))}))))

View File

@@ -9,24 +9,27 @@
[clj-time.predicates :as pred] [clj-time.predicates :as pred]
[clojure.data.json :as json] [clojure.data.json :as json]
[compojure.core :refer [GET PUT POST context defroutes [compojure.core :refer [GET PUT POST context defroutes
wrap-routes]]) wrap-routes]]
[clojure.tools.logging :as log]
[unilog.context :as lc])
(:import (org.joda.time DateTime))) (:import (org.joda.time DateTime)))
(defroutes routes (defroutes routes
(context "/events" [] (context "/events" []
(POST "/yodlee-import" {:keys [query-params headers body] :as x} (POST "/yodlee-import" {:keys [query-params headers body] :as x}
(let [notification-type (get headers "x-amz-sns-message-type")] (lc/with-context {:source "Import yodlee transactions"}
(println "Received notification " notification-type) (let [notification-type (get headers "x-amz-sns-message-type")]
(if (= "SubscriptionConfirmation" notification-type) (log/info "Received notification " notification-type)
(do (if (= "SubscriptionConfirmation" notification-type)
(println "Responding to confirmation" ) (do
(let [json (json/read-str (slurp body))] (log/info "Responding to confirmation" )
(println json) (let [json (json/read-str (slurp body))]
(http/get (get json "SubscribeURL")))) (log/info json)
(do (http/get (get json "SubscribeURL"))))
(println "importing from yodlee") (do
(yodlee-import/do-import)))) (log/info "importing from yodlee")
(yodlee-import/do-import)))))
{:status 200 {:status 200
:body "{}" :body "{}"

View File

@@ -1,10 +1,12 @@
(ns auto-ap.routes.graphql (ns auto-ap.routes.graphql
(:require [auto-ap.routes.utils :refer [wrap-secure wrap-spec]] (:require [auto-ap.routes.utils :refer [wrap-secure wrap-spec]]
[auto-ap.graphql :as ql] [auto-ap.graphql :as ql]
[auto-ap.logging :refer [warn-event]]
[buddy.auth :refer [throw-unauthorized]] [buddy.auth :refer [throw-unauthorized]]
[clojure.edn :as edn] [clojure.edn :as edn]
[compojure.core :refer [GET POST PUT context defroutes [compojure.core :refer [GET POST PUT context defroutes
wrap-routes]])) wrap-routes]]
[clojure.tools.logging :as log]))
(defn handle-graphql [{:keys [request-method query-params body edn-params method] :as r}] (defn handle-graphql [{:keys [request-method query-params body edn-params method] :as r}]
(when (= "none" (:user/role (:identity r))) (when (= "none" (:user/role (:identity r)))
(throw-unauthorized)) (throw-unauthorized))
@@ -14,19 +16,23 @@
edn/read-string) edn/read-string)
body (some-> r :body slurp)] body (some-> r :body slurp)]
{:status 200 {:status 200
:body (pr-str (ql/query (:identity r) (doto (if (= request-method :get) (query-params "query") body) println) variables )) :body (pr-str (ql/query (:identity r) (if (= request-method :get) (query-params "query") body) variables ))
:headers {"Content-Type" "application/edn"}}) :headers {"Content-Type" "application/edn"}})
(catch Throwable e (catch Throwable e
(if-let [result (:result (ex-data e))] (if-let [result (:result (ex-data e))]
{:status 400 (do (log/warn "Graphql Result error" e)
:body (pr-str result)
:headers {"Content-Type" "application/edn"}}
(if-let [message (:validation-error (ex-data e) )]
{:status 400 {:status 400
:body (pr-str {:errors [(merge {:message message} (ex-data e))]}) :body (pr-str result)
:headers {"Content-Type" "application/edn"}} :headers {"Content-Type" "application/edn"}})
(do (println e) (if-let [message (:validation-error (ex-data e) )]
(do
(warn-event "GraphQL Validation error" {:message message
:data e})
{:status 400
:body (pr-str {:errors [(merge {:message message} (ex-data e))]})
:headers {"Content-Type" "application/edn"}})
(do (log/error "GraphQL error" e)
{:status 500 {:status 500
:body (pr-str {:errors [(merge {:message (.getMessage e)} (ex-data e))]}) :body (pr-str {:errors [(merge {:message (.getMessage e)} (ex-data e))]})
:headers {"Content-Type" "application/edn"}})))))) :headers {"Content-Type" "application/edn"}}))))))

View File

@@ -19,7 +19,10 @@
wrap-routes]] wrap-routes]]
[clojure.string :as str] [clojure.string :as str]
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.data.csv :as csv])) [clojure.data.csv :as csv]
[unilog.context :as lc]
[clojure.tools.logging :as log]
[auto-ap.logging :refer [info-event]]))
(defn reset-id [i] (defn reset-id [i]
(update i :invoice-number (update i :invoice-number
@@ -203,88 +206,91 @@
{:vendor-code vendor-code}))))) {:vendor-code vendor-code})))))
(defn import-uploaded-invoice [client forced-location forced-vendor imports] (defn import-uploaded-invoice [client forced-location forced-vendor imports]
(let [clients (d-clients/get-all) (lc/with-context {:area "upload-invoice"}
transactions (reduce (fn [result {:keys [invoice-number customer-identifier account-number total date vendor-code text full-text] :as info}] (let [clients (d-clients/get-all)
(println "searching for" vendor-code) transactions (reduce (fn [result {:keys [invoice-number customer-identifier account-number total date vendor-code text full-text] :as info}]
(let [ _ (println "matching" customer-identifier)
matching-client (or (and account-number
(parse/best-match clients account-number 0.0))
(and customer-identifier
(parse/best-match clients customer-identifier))
(if client
(first (filter (fn [c]
(= (:db/id c) (Long/parseLong client)))
clients))))
_ (when-not matching-client
(throw (ex-info (str "Searched clients for '" customer-identifier "'. No client found in file. Select a client first.")
{:invoice-number invoice-number
:customer-identifier customer-identifier
:vendor-code vendor-code})))
matching-vendor (match-vendor vendor-code forced-vendor)
_ (println "invoice \"" invoice-number "\" matches client " (:client/name matching-client) " (" (:db/id matching-client) ")")
matching-location (or (when-not (str/blank? forced-location)
forced-location)
(parse/best-location-match matching-client text full-text))
[existing-id existing-outstanding-balance existing-status import-status] (when (and matching-client matching-location)
(try
(->> (d/query
(cond-> {:query {:find ['?e '?outstanding-balance '?status '?import-status2]
:in ['$ '?invoice-number '?vendor '?client]
:where '[[?e :invoice/invoice-number ?invoice-number]
[?e :invoice/vendor ?vendor]
[?e :invoice/client ?client]
[?e :invoice/outstanding-balance ?outstanding-balance]
[?e :invoice/status ?status]
[?e :invoice/import-status ?import-status]
[?import-status :db/ident ?import-status2]]}
:args [(d/db (d/connect uri)) invoice-number (:db/id matching-vendor) (:db/id matching-client)]}))
first)
(catch Exception e
(throw (ex-info (str "Failed to find potential matching invoice with"
" invoice " invoice-number
" vendor " matching-vendor
" client " (:client/name matching-client)
". "
(.toString e))
{:args [ invoice-number matching-vendor (:db/id matching-client)]})))
))
automatically-paid-for (set (map :db/id (:vendor/automatically-paid-when-due matching-vendor)))]
(cond (let [
(not (and matching-location matching-client)) matching-client (or (and account-number
result (parse/best-match clients account-number 0.0))
(and customer-identifier
(parse/best-match clients customer-identifier))
(if client
(first (filter (fn [c]
(= (:db/id c) (Long/parseLong client)))
clients))))
_ (when-not matching-client
(throw (ex-info (str "Searched clients for '" customer-identifier "'. No client found in file. Select a client first.")
{:invoice-number invoice-number
:customer-identifier customer-identifier
:vendor-code vendor-code})))
(= :import-status/imported import-status) matching-vendor (match-vendor vendor-code forced-vendor)
result
:else
(conj result (cond-> (remove-nils #:invoice {:invoice/client (:db/id matching-client) _ (info-event "Found match for invoice" {:invoice-number invoice-number
:invoice/client-identifier customer-identifier :vendor (select-keys matching-vendor [:vendor/name :db/id])
:invoice/vendor (:db/id matching-vendor) :client (select-keys matching-client [:client/name :db/id])})
:invoice/invoice-number invoice-number matching-location (or (when-not (str/blank? forced-location)
:invoice/automatically-paid-when-due (boolean (automatically-paid-for (:db/id matching-client))) forced-location)
:invoice/total (Double/parseDouble total) (parse/best-location-match matching-client text full-text))
:invoice/date (to-date date) [existing-id existing-outstanding-balance existing-status import-status] (when (and matching-client matching-location)
:invoice/import-status :import-status/pending (try
:invoice/outstanding-balance (or existing-outstanding-balance (Double/parseDouble total)) (->> (d/query
:invoice/status (or existing-status :invoice-status/unpaid) (cond-> {:query {:find ['?e '?outstanding-balance '?status '?import-status2]
:invoice/expense-accounts (when-not existing-id [#:invoice-expense-account {:account (d-vendors/account-for-client-id matching-vendor (:db/id matching-client)) :in ['$ '?invoice-number '?vendor '?client]
:location matching-location :where '[[?e :invoice/invoice-number ?invoice-number]
:amount (Double/parseDouble total)}]) [?e :invoice/vendor ?vendor]
:db/id existing-id [?e :invoice/client ?client]
}) [?e :invoice/outstanding-balance ?outstanding-balance]
(:vendor/terms matching-vendor) (assoc :invoice/due (coerce/to-date [?e :invoice/status ?status]
(time/plus date (time/days (d-vendors/terms-for-client-id matching-vendor (:db/id matching-client))))))))) [?e :invoice/import-status ?import-status]
)) [?import-status :db/ident ?import-status2]]}
[] :args [(d/db (d/connect uri)) invoice-number (:db/id matching-vendor) (:db/id matching-client)]}))
imports)] first)
(when-not (seq transactions) (catch Exception e
(throw (ex-info "No invoices found." (throw (ex-info (str "Failed to find potential matching invoice with"
{:imports (str imports)}))) " invoice " invoice-number
@(d/transact (d/connect uri) (vec (set transactions))))) " vendor " matching-vendor
" client " (:client/name matching-client)
". "
(.toString e))
{:args [ invoice-number matching-vendor (:db/id matching-client)]})))
))
automatically-paid-for (set (map :db/id (:vendor/automatically-paid-when-due matching-vendor)))]
(cond
(not (and matching-location matching-client))
result
(= :import-status/imported import-status)
result
:else
(conj result (cond-> (remove-nils #:invoice {:invoice/client (:db/id matching-client)
:invoice/client-identifier customer-identifier
:invoice/vendor (:db/id matching-vendor)
:invoice/invoice-number invoice-number
:invoice/automatically-paid-when-due (boolean (automatically-paid-for (:db/id matching-client)))
:invoice/total (Double/parseDouble total)
:invoice/date (to-date date)
:invoice/import-status :import-status/pending
:invoice/outstanding-balance (or existing-outstanding-balance (Double/parseDouble total))
:invoice/status (or existing-status :invoice-status/unpaid)
:invoice/expense-accounts (when-not existing-id [#:invoice-expense-account {:account (d-vendors/account-for-client-id matching-vendor (:db/id matching-client))
:location matching-location
:amount (Double/parseDouble total)}])
:db/id existing-id
})
(:vendor/terms matching-vendor) (assoc :invoice/due (coerce/to-date
(time/plus date (time/days (d-vendors/terms-for-client-id matching-vendor (:db/id matching-client)))))))))
))
[]
imports)]
(when-not (seq transactions)
(throw (ex-info "No invoices found."
{:imports (str imports)})))
@(d/transact (d/connect uri) (vec (set transactions))))))
(defn validate-account-rows [rows code->existing-account] (defn validate-account-rows [rows code->existing-account]
@@ -318,7 +324,6 @@
:in ['$ '?z] :in ['$ '?z]
:where [['?e :client/code '?z]]} :where [['?e :client/code '?z]]}
:args [(d/db (d/connect uri)) customer]}))) :args [(d/db (d/connect uri)) customer]})))
_ (println client-id)
headers (map read-string header) headers (map read-string header)
code->existing-account (by :account/numeric-code (map first (d/query {:query {:find ['(pull ?e [:account/numeric-code code->existing-account (by :account/numeric-code (map first (d/query {:query {:find ['(pull ?e [:account/numeric-code
{:account/applicability [:db/ident]} {:account/applicability [:db/ident]}
@@ -427,7 +432,7 @@
(map (parse-or-error :amount parse-amount)) (map (parse-or-error :amount parse-amount))
(map (parse-or-error :date parse-date))) (map (parse-or-error :date parse-date)))
error-rows (filter :errors rows) error-rows (filter :errors rows)
_ (println "importing raw transactions" rows) _ (log/info "Importing " (count rows) "raw transactions")
raw-transactions (vec (->> rows raw-transactions (vec (->> rows
(filter #(not (seq (:errors %))) ) (filter #(not (seq (:errors %))) )
(map (fn [{:keys [description-original client-code status high-level-category amount bank-account-code date]}] (map (fn [{:keys [description-original client-code status high-level-category amount bank-account-code date]}]
@@ -445,7 +450,7 @@
:errors (map #(dissoc % :date) error-rows)}) :errors (map #(dissoc % :date) error-rows)})
:headers {"Content-Type" "application/edn"}}) :headers {"Content-Type" "application/edn"}})
(catch Exception e (catch Exception e
(println e) (log/error e)
{:status 500 {:status 500
:body (pr-str {:message (.getMessage e) :body (pr-str {:message (.getMessage e)
:error (.toString e) :error (.toString e)
@@ -469,18 +474,18 @@
vendor (some-> (or vendor vendor-2) vendor (some-> (or vendor vendor-2)
(Long/parseLong)) (Long/parseLong))
{:keys [filename tempfile]} files] {:keys [filename tempfile]} files]
#_(println params (.getPath tempfile) filename) (lc/with-context {:parsing-file filename}
(try (try
(import-uploaded-invoice client location vendor (parse/parse-file (.getPath tempfile) filename)) (import-uploaded-invoice client location vendor (parse/parse-file (.getPath tempfile) filename))
{:status 200 {:status 200
:body (pr-str {}) :body (pr-str {})
:headers {"Content-Type" "application/edn"}} :headers {"Content-Type" "application/edn"}}
(catch Exception e (catch Exception e
{:status 500 {:status 500
:body (pr-str {:message (.getMessage e) :body (pr-str {:message (.getMessage e)
:error (.toString e) :error (.toString e)
:data (ex-data e)}) :data (ex-data e)})
:headers {"Content-Type" "application/edn"}})))) :headers {"Content-Type" "application/edn"}})))))
(POST "/upload-integreat" (POST "/upload-integreat"
{{:keys [excel-rows]} :edn-params user :identity} {{:keys [excel-rows]} :edn-params user :identity}
@@ -515,7 +520,6 @@
files-2 "file"} :params :as params files-2 "file"} :params :as params
user :identity} user :identity}
(let [files (or files files-2) (let [files (or files files-2)
_ (println files)
{:keys [filename tempfile]} files] {:keys [filename tempfile]} files]
(assert-admin user) (assert-admin user)
(try (try
@@ -524,7 +528,7 @@
:body (pr-str {}) :body (pr-str {})
:headers {"Content-Type" "application/edn"}} :headers {"Content-Type" "application/edn"}}
(catch Exception e (catch Exception e
(println e) (log/error e)
{:status 500 {:status 500
:body (pr-str {:message (.getMessage e) :body (pr-str {:message (.getMessage e)
:error (.toString e) :error (.toString e)
@@ -545,7 +549,7 @@
:body (import-account-overrides client (.getPath tempfile)) :body (import-account-overrides client (.getPath tempfile))
:headers {"Content-Type" "application/json"}} :headers {"Content-Type" "application/json"}}
(catch Exception e (catch Exception e
(println e) (log/error e)
{:status 500 {:status 500
:body {:message (.getMessage e) :body {:message (.getMessage e)
:data (ex-data e)} :data (ex-data e)}

View File

@@ -1,17 +1,18 @@
(ns auto-ap.routes.yodlee (ns auto-ap.routes.yodlee
(:require (:require
[auto-ap.graphql :as graphql] [auto-ap.graphql :as graphql]
[clj-http.client :as http] [clj-http.client :as http]
[auto-ap.yodlee.core :as yodlee] [auto-ap.yodlee.core :as yodlee]
[auto-ap.graphql.utils :refer [->graphql assert-admin]] [auto-ap.graphql.utils :refer [->graphql assert-admin]]
[auto-ap.routes.utils :refer [wrap-secure]] [auto-ap.routes.utils :refer [wrap-secure]]
[clj-time.coerce :refer [to-date]] [clj-time.coerce :refer [to-date]]
[ring.middleware.json :refer [wrap-json-response]] [ring.middleware.json :refer [wrap-json-response]]
[compojure.core :refer [GET POST context defroutes wrap-routes]] [compojure.core :refer [GET POST context defroutes wrap-routes]]
[clojure.string :as str] [clojure.string :as str]
[config.core :refer [env]] [config.core :refer [env]]
))
[clojure.tools.logging :as log]))
(defroutes routes (defroutes routes
(wrap-routes (wrap-routes
@@ -53,7 +54,7 @@
:headers {"Content-Type" "application/edn"} :headers {"Content-Type" "application/edn"}
:body (pr-str (yodlee/reauthenticate (Long/parseLong id) data)) }) :body (pr-str (yodlee/reauthenticate (Long/parseLong id) data)) })
(catch Exception e (catch Exception e
(println e) (log/error e)
{:status 500 {:status 500
:headers {"Content-Type" "application/edn"} :headers {"Content-Type" "application/edn"}
:body (pr-str {:message (.getMessage e) :body (pr-str {:message (.getMessage e)

View File

@@ -7,14 +7,19 @@
[nrepl.server :refer [start-server stop-server]] [nrepl.server :refer [start-server stop-server]]
[config.core :refer [env]] [config.core :refer [env]]
[ring.adapter.jetty :refer [run-jetty]] [ring.adapter.jetty :refer [run-jetty]]
[clojure.tools.logging :as log]
[unilog.config]
[mount.core :as mount]) [mount.core :as mount])
(:gen-class)) (:gen-class))
(mount/defstate port :start (Integer/parseInt (or (env :port) "3000")))
(mount/defstate jetty
:start (run-jetty app {:port port :join? false})
:stop (.stop jetty)
)
(defn -main [& args] (defn -main [& args]
(start-server :port 9000 :bind "0.0.0.0" #_#_:handler (cider-nrepl-handler)) (start-server :port 9000 :bind "0.0.0.0" #_#_:handler (cider-nrepl-handler))
(let [port (Integer/parseInt (or (env :port) "3000"))] (mount/start))
(mount/start)
#_(future (always-process-sqs))
(run-jetty app {:port port :join? false})))

View File

@@ -2,7 +2,10 @@
(:require [clj-http.client :as client] (:require [clj-http.client :as client]
[auto-ap.utils :refer [by]] [auto-ap.utils :refer [by]]
[cemerick.url :as u] [cemerick.url :as u]
[unilog.context :as lc]
[clojure.tools.logging :as log]
[clojure.data.json :as json] [clojure.data.json :as json]
[clojure.core.async :as async]
[config.core :refer [env]] [config.core :refer [env]]
[mount.core :as mount] [mount.core :as mount]
[yang.scheduler :as scheduler])) [yang.scheduler :as scheduler]))
@@ -92,9 +95,7 @@
(-> (str (:yodlee-base-url env) "/transactions?top=" batch-size "&skip=" skip) (-> (str (:yodlee-base-url env) "/transactions?top=" batch-size "&skip=" skip)
(client/get {:headers (doto (client/get {:headers (merge base-headers {"Authorization" (auth-header cob-session user-session)})
(merge base-headers {"Authorization" (auth-header cob-session user-session)})
println)
:as :json}) :as :json})
:body :body
:transaction :transaction
@@ -114,9 +115,7 @@
(-> (str (:yodlee-base-url env) "/providerAccounts") (-> (str (:yodlee-base-url env) "/providerAccounts")
(client/get {:headers (doto (client/get {:headers (merge base-headers {"Authorization" (auth-header cob-session user-session)})
(merge base-headers {"Authorization" (auth-header cob-session user-session)})
println)
:as :json}) :as :json})
:body :body
:providerAccount :providerAccount
@@ -131,9 +130,7 @@
(-> (str (:yodlee-base-url env) "/providerAccounts/" id) (-> (str (:yodlee-base-url env) "/providerAccounts/" id)
(client/get {:headers (doto (client/get {:headers (merge base-headers {"Authorization" (auth-header cob-session user-session)})
(merge base-headers {"Authorization" (auth-header cob-session user-session)})
println)
:as :json}) :as :json})
:body :body
:providerAccount))) :providerAccount)))
@@ -145,9 +142,7 @@
(-> (str (:yodlee-base-url env) "/providerAccounts/" id ) (-> (str (:yodlee-base-url env) "/providerAccounts/" id )
(client/get {:headers (doto (client/get {:headers (merge base-headers {"Authorization" (auth-header cob-session user-session)})
(merge base-headers {"Authorization" (auth-header cob-session user-session)})
println)
:query-params {"include" "credentials,questions,preferences"} :query-params {"include" "credentials,questions,preferences"}
:as :json}) :as :json})
:body :body
@@ -161,9 +156,7 @@
(-> (str (:yodlee-base-url env) "/providerAccounts?providerAccountIds=" pa) (-> (str (:yodlee-base-url env) "/providerAccounts?providerAccountIds=" pa)
(client/put {:headers (doto (client/put {:headers (merge base-headers {"Authorization" (auth-header cob-session user-session)})
(merge base-headers {"Authorization" (auth-header cob-session user-session)})
println)
:body "{\"dataSetName\": [\"BASIC_AGG_DATA\"]}" :body "{\"dataSetName\": [\"BASIC_AGG_DATA\"]}"
:as :json})))) :as :json}))))
@@ -179,9 +172,7 @@
get-transaction-batch (fn [skip] get-transaction-batch (fn [skip]
(-> (str (:yodlee-base-url env) "/transactions?top=" batch-size "&skip=" skip "&accountId=" account) (-> (str (:yodlee-base-url env) "/transactions?top=" batch-size "&skip=" skip "&accountId=" account)
(client/get {:headers (doto (client/get {:headers (merge base-headers {"Authorization" (auth-header cob-session user-session)})
(merge base-headers {"Authorization" (auth-header cob-session user-session)})
println)
:as :json}) :as :json})
:body :body
:transaction :transaction
@@ -199,11 +190,8 @@
user-session (login-user cob-session)] user-session (login-user cob-session)]
(-> (str (:yodlee-base-url env) "/transactions/count?accountId=" account) (-> (str (:yodlee-base-url env) "/transactions/count?accountId=" account)
(doto println)
(client/get {:headers (doto (client/get {:headers (merge base-headers {"Authorization" (auth-header cob-session user-session)})
(merge base-headers {"Authorization" (auth-header cob-session user-session)})
println)
:as :json}) :as :json})
:body :body
:transaction :transaction
@@ -236,34 +224,55 @@
:as :json}) :as :json})
:body))) :body)))
(defn get-provider-accounts-with-details [] (defn get-provider-accounts-with-details []
(let [provider-accounts (get-provider-accounts)] (let [provider-accounts (get-provider-accounts)]
(reduce (let [concurrent 20
(fn [pas pa] output-chan (async/chan)]
(conj pas (try (get-provider-account-detail (:id pa)) (async/pipeline-blocking concurrent
(catch Exception e output-chan
pa)))) (map (fn [provider-account]
[] (lc/with-context {:provider-account-id (:id provider-account)}
provider-accounts))) (log/info "fetching details for provider" (:id provider-account))
(get-provider-account-detail (:id provider-account)))))
(async/to-chan provider-accounts))
(async/<!! (async/into [] output-chan)))))
(defn concurrent-get-accounts-for-providers [provider-account-ids]
(let [concurrent 20
output-chan (async/chan)]
(async/pipeline-blocking concurrent
output-chan
(map (fn [provider-account-id]
(lc/with-context {:provider-account-id provider-account-id}
(log/info "fetching accounts for provider" provider-account-id)
[provider-account-id
(get-accounts-for-provider-account provider-account-id)])))
(async/to-chan provider-account-ids))
(async/<!! (async/into {} output-chan))))
(defn get-provider-accounts-with-accounts [] (defn get-provider-accounts-with-accounts []
(let [provider-accounts (by :id (get-provider-accounts-with-details)) (let [provider-accounts (by :id (get-provider-accounts-with-details))
accounts (get-accounts)] accounts (concurrent-get-accounts-for-providers (keys provider-accounts))]
(->> accounts (->> accounts
(reduce (reduce
(fn [provider-accounts a] (fn [provider-accounts [which accounts]]
(update-in provider-accounts [(:providerAccountId a) :accounts] conj a)) provider-accounts) (assoc-in provider-accounts [which :accounts] accounts))
provider-accounts)
vals))) vals)))
(mount/defstate in-memory-cache (mount/defstate in-memory-cache
:start (doto (atom (get-provider-accounts-with-accounts)) println)) :start (atom []))
(defn refresh-in-memory-cache [] (defn refresh-in-memory-cache []
(try (lc/with-context {:source "refreshing-in-memory-cache"}
(println "Refreshing Yodlee in memory cache") (try
(reset! in-memory-cache (get-provider-accounts-with-accounts)) (log/info "Refreshing Yodlee in memory cache")
(catch Exception e (reset! in-memory-cache (get-provider-accounts-with-accounts))
(println e))))
(catch Exception e
(log/error e)))))
(mount/defstate in-memory-cache-worker (mount/defstate in-memory-cache-worker
:start (scheduler/every (* 5 60 1000) refresh-in-memory-cache) :start (scheduler/every (* 5 60 1000) refresh-in-memory-cache)

View File

@@ -12,7 +12,9 @@
[auto-ap.time :as time] [auto-ap.time :as time]
[auto-ap.datomic.transaction-rules :as tr] [auto-ap.datomic.transaction-rules :as tr]
[auto-ap.rule-matching :as rm] [auto-ap.rule-matching :as rm]
[clojure.string :as str])) [clojure.string :as str]
[unilog.context :as lc]
[clojure.tools.logging :as log]))
@@ -149,29 +151,31 @@
:client/_bank-accounts client)))))))) :client/_bank-accounts client))))))))
(defn manual-import [manual-transactions] (defn manual-import [manual-transactions]
(let [transformed-transactions (->> manual-transactions (lc/with-context {:source "manual import"}
(filter #(= "posted" (:status %))) (let [transformed-transactions (->> manual-transactions
(group-by #(select-keys % [:date :description-original :amount])) (filter #(= "posted" (:status %)))
(vals) (group-by #(select-keys % [:date :description-original :amount]))
(mapcat (fn [transaction-group] (vals)
(map (mapcat (fn [transaction-group]
(fn [index {:keys [date description-original high-level-category amount bank-account-id client-id] :as transaction}] (map
{:id (str date "-" bank-account-id "-" description-original "-" amount "-" index "-" client-id) (fn [index {:keys [date description-original high-level-category amount bank-account-id client-id] :as transaction}]
:bank-account-id bank-account-id {:id (str date "-" bank-account-id "-" description-original "-" amount "-" index "-" client-id)
:date (time/unparse date "YYYY-MM-dd") :bank-account-id bank-account-id
:amount {:amount amount} :date (time/unparse date "YYYY-MM-dd")
:description {:original description-original :amount {:amount amount}
:simple high-level-category} :description {:original description-original
:status "POSTED"}) :simple high-level-category}
(range) :status "POSTED"})
transaction-group)))) (range)
all-rules (tr/get-all) transaction-group))))
all-bank-accounts (by :db/id (get-all-bank-accounts)) all-rules (tr/get-all)
transaction->bank-account (comp all-bank-accounts :bank-account-id)] all-bank-accounts (by :db/id (get-all-bank-accounts))
(println "importing manual transactions" transformed-transactions) transaction->bank-account (comp all-bank-accounts :bank-account-id)]
(let [result (batch-transact (log/info "Importing " (count transformed-transactions) " manual transactions")
(transactions->txs transformed-transactions transaction->bank-account (rm/rule-applying-fn all-rules) (get-existing)))]
(println "imported " (count result))))) (let [result (batch-transact
(transactions->txs transformed-transactions transaction->bank-account (rm/rule-applying-fn all-rules) (get-existing)))]
(log/info "Imported " (count result) " manual transactions")))))
(defn do-import (defn do-import
([] ([]

View File

@@ -3,6 +3,8 @@
[auto-ap.utils :refer [by]] [auto-ap.utils :refer [by]]
#_[auto-ap.ledger :as l] #_[auto-ap.ledger :as l]
[mount.core :as mount]
[auto-ap.server ]
[datomic.api :as d] [datomic.api :as d]
[clojure.data.csv :as csv] [clojure.data.csv :as csv]
[clj-time.coerce :as c] [clj-time.coerce :as c]
@@ -360,3 +362,8 @@
'(not-join [?i] [?e :journal-entry/original-entity ?i])]} '(not-join [?i] [?e :journal-entry/original-entity ?i])]}
:args [(d/db (d/connect uri))]})]) :args [(d/db (d/connect uri))]})])
(defn go []
(require '[mount.core :as mount])
(require '[auto-ap.server])
(mount/start-without #'auto-ap.server/jetty))

View File

@@ -52,7 +52,6 @@
::params-change ::params-change
[with-user (re-frame/inject-cofx ::inject/sub [::params])] [with-user (re-frame/inject-cofx ::inject/sub [::params])]
(fn [{:keys [user ::params db]} [_]] (fn [{:keys [user ::params db]} [_]]
(println "execute")
{:db (-> db {:db (-> db
(assoc-in [::last-params] params) (assoc-in [::last-params] params)
(assoc-in [:status :loading] true)) (assoc-in [:status :loading] true))