Add vendor pre-population for bulk code and individual edit forms

- Add vendor-changed HTMX handlers for both bulk code and individual edit
- Pre-populate default account at 100% when vendor is selected and no accounts exist
- Fix render-accounts-section to render from step-params correctly
- Change bulk code vendor-changed from hx-get to hx-post to include form data
- Add routes for vendor-changed endpoints
- Update e2e tests to cover vendor pre-population
- Run lein cljfmt fix across codebase
This commit is contained in:
2026-05-21 14:45:19 -07:00
parent 8bd0cee1b1
commit ba87805d4c
210 changed files with 8694 additions and 9627 deletions

View File

@@ -36,15 +36,15 @@
(.encodeToString (java.util.Base64/getEncoder) (.toByteArray raw))))
(defn gunzip [b64]
(let [raw-bytes (.decode (java.util.Base64/getDecoder) b64)
raw (java.io.ByteArrayInputStream. raw-bytes)
out (java.io.ByteArrayOutputStream.)]
(with-open [compressed (-> raw
(io/input-stream)
(java.util.zip.GZIPInputStream.))]
(io/input-stream)
(java.util.zip.GZIPInputStream.))]
(io/copy compressed out))
(edn/read-string (.toString out))))
(defn user->jwt [user oauth-token]
@@ -94,8 +94,8 @@
(if-let [jwt (user->jwt user token)]
{:status 301
:headers {"Location" (str (or (not-empty state)
(bidi/path-for ssr-routes/only-routes
:headers {"Location" (str (or (not-empty state)
(bidi/path-for ssr-routes/only-routes
::dashboard/page)) "?jwt="
(jwt/sign jwt
(:jwt-secret env)

View File

@@ -76,143 +76,137 @@
(double? v)
(str v)
:else
v)
]))
v)]))
m))
(defn export-invoices [{:keys [query-params identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:invoice"}}]
{:body
(list (into (list)
(map datomic-map->graphql-map)
(map first (dc/q '[:find (pull ?i [:db/id :invoice/total :invoice/outstanding-balance :invoice/invoice-number :invoice/date :invoice/original-id
{ :invoice/status [:db/ident]
:invoice/payments
[:invoice-payment/amount
{:invoice-payment/payment [:payment/check-number
:payment/memo
{:payment/bank_account [:bank-account/id :bank-account/name :bank-account/number :bank-account/bank-name :bank-account/bank-code :bank-account/code]}]}]
:invoice/vendor [:vendor/name
:db/id
{:vendor/primary-contact [:contact/name]
:vendor/address [:address/street1 :address/city :address/state :address/zip]}]
:invoice/expense-accounts [:db/id
:invoice-expense-account/amount
:invoice-expense-account/id
:invoice-expense-account/location
{:invoice-expense-account/account
[:db/id :account/numeric-code :account/name]}]
:invoice/client [:client/name :db/id :client/code :client/locations]}])
:in $ ?c
:where [?i :invoice/client ?c]]
{:body
(list (into (list)
(map datomic-map->graphql-map)
(map first (dc/q '[:find (pull ?i [:db/id :invoice/total :invoice/outstanding-balance :invoice/invoice-number :invoice/date :invoice/original-id
{:invoice/status [:db/ident]
:invoice/payments
[:invoice-payment/amount
{:invoice-payment/payment [:payment/check-number
:payment/memo
{:payment/bank_account [:bank-account/id :bank-account/name :bank-account/number :bank-account/bank-name :bank-account/bank-code :bank-account/code]}]}]
:invoice/vendor [:vendor/name
:db/id
{:vendor/primary-contact [:contact/name]
:vendor/address [:address/street1 :address/city :address/state :address/zip]}]
:invoice/expense-accounts [:db/id
:invoice-expense-account/amount
:invoice-expense-account/id
:invoice-expense-account/location
{:invoice-expense-account/account
[:db/id :account/numeric-code :account/name]}]
:invoice/client [:client/name :db/id :client/code :client/locations]}])
:in $ ?c
:where [?i :invoice/client ?c]]
(dc/db conn)
[:client/code (query-params "client-code")]))))}))
(dc/db conn)
[:client/code (query-params "client-code")]))))}))
(defn export-payments [{:keys [query-params identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:payment"}}]
(let [query [[:all_payments
{:client-code (query-params "client-code")
:original-id (query-params "original")}
[:id :check-number :amount :memo :date :status :type :original-id
[:invoices [[:invoice [:id :original-id]] :amount]]
[:bank-account [:number :code :bank-name :bank-code :id]]
[:vendor [:name :id [:primary-contact [:name :email :phone]] [:default-account [:name :numeric-code :id]] [:address [:street1 :city :state :zip]]]]
[:client [:id :name :code]]
]]]
payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}) {:clients [ [:client/code (query-params "client-code")]]})]
{:body
(list (:all-payments (:data payments)))})))
(let [query [[:all_payments
{:client-code (query-params "client-code")
:original-id (query-params "original")}
[:id :check-number :amount :memo :date :status :type :original-id
[:invoices [[:invoice [:id :original-id]] :amount]]
[:bank-account [:number :code :bank-name :bank-code :id]]
[:vendor [:name :id [:primary-contact [:name :email :phone]] [:default-account [:name :numeric-code :id]] [:address [:street1 :city :state :zip]]]]
[:client [:id :name :code]]]]]
payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}) {:clients [[:client/code (query-params "client-code")]]})]
{:body
(list (:all-payments (:data payments)))})))
(defn export-sales [{:keys [query-params identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:sales"}}]
(let [query [[:all_sales_orders
(cond-> {:client-code (query-params "client-code")}
(query-params "after") (assoc :date-range {:start (query-params "after")
:end nil}))
[:id
:location
:external_id
:total
:tip
:tax
:discount
:returns
:service_charge
:date
[:charges [:type_name :total :tip]]
[:line_items [:item_name :total :tax :discount :category]]
[:client [:id :name :code]]]]]
payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))
parsedouble #(some-> % Double/parseDouble) ]
{:body
(seq (map
(fn [s]
(-> s
(assoc :utc_date (:date s))
(update :date (fn [d]
(coerce/to-string (coerce/to-local-date-time (time/to-time-zone (coerce/to-date-time d) (time/time-zone-for-id "America/Los_Angeles"))))))
(update :total parsedouble)
(update :tax parsedouble)
(update :discount parsedouble)
(update :tip parsedouble)
(update :line-items (fn [lis]
(map
(fn [li]
(-> li
(update :tax parsedouble)
(update :discount parsedouble)
(update :total parsedouble)))
lis)))
(update :charges (fn [charges]
(map
(fn [charge]
(-> charge
(update :tip parsedouble)
(update :total parsedouble)))
charges)))))
(:all-sales-orders (:data payments))))})))
(let [query [[:all_sales_orders
(cond-> {:client-code (query-params "client-code")}
(query-params "after") (assoc :date-range {:start (query-params "after")
:end nil}))
[:id
:location
:external_id
:total
:tip
:tax
:discount
:returns
:service_charge
:date
[:charges [:type_name :total :tip]]
[:line_items [:item_name :total :tax :discount :category]]
[:client [:id :name :code]]]]]
payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))
parsedouble #(some-> % Double/parseDouble)]
{:body
(seq (map
(fn [s]
(-> s
(assoc :utc_date (:date s))
(update :date (fn [d]
(coerce/to-string (coerce/to-local-date-time (time/to-time-zone (coerce/to-date-time d) (time/time-zone-for-id "America/Los_Angeles"))))))
(update :total parsedouble)
(update :tax parsedouble)
(update :discount parsedouble)
(update :tip parsedouble)
(update :line-items (fn [lis]
(map
(fn [li]
(-> li
(update :tax parsedouble)
(update :discount parsedouble)
(update :total parsedouble)))
lis)))
(update :charges (fn [charges]
(map
(fn [charge]
(-> charge
(update :tip parsedouble)
(update :total parsedouble)))
charges)))))
(:all-sales-orders (:data payments))))})))
(defn export-expected-deposits [{:keys [query-params identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:deposit"}}]
(let [query [[:all_expected_deposits
(cond-> {:client-code (query-params "client-code")}
(query-params "after") (assoc :date-range {:start (query-params "after")
:end nil}))
[:id
[:client [:id :name :code]]
:location
:external_id
:total
:fee
:date]]]
payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))]
{:body
(seq (map
(fn [d]
(-> d
(update :fee #(some-> % Double/parseDouble))
(update :total #(some-> % Double/parseDouble))))
(:all-expected-deposits (:data payments))))})))
(let [query [[:all_expected_deposits
(cond-> {:client-code (query-params "client-code")}
(query-params "after") (assoc :date-range {:start (query-params "after")
:end nil}))
[:id
[:client [:id :name :code]]
:location
:external_id
:total
:fee
:date]]]
payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))]
{:body
(seq (map
(fn [d]
(-> d
(update :fee #(some-> % Double/parseDouble))
(update :total #(some-> % Double/parseDouble))))
(:all-expected-deposits (:data payments))))})))
(generate/add-encoder org.joda.time.DateTime
(fn [c jsonGenerator]
(.writeString jsonGenerator (str c))))
(fn [c jsonGenerator]
(.writeString jsonGenerator (str c))))
(defn export-clients[{:keys [identity]}]
(defn export-clients [{:keys [identity]}]
(assert-admin identity)
{:body (into []
(map <-graphql)
@@ -221,57 +215,56 @@
(defn export-vendors [{:keys [identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{"export:vendors"}}]
{:body
(map <-graphql (->> (dc/q '[:find ?e
:in $
:where [?e :vendor/name]]
(dc/db conn))
(map first)
(pull-many (dc/db conn) vendor/default-read)))}))
{:body
(map <-graphql (->> (dc/q '[:find ?e
:in $
:where [?e :vendor/name]]
(dc/db conn))
(map first)
(pull-many (dc/db conn) vendor/default-read)))}))
(defn export-company-vendors [{:keys [identity query-params]}]
(statsd/time! [(str "export.time") {:tags #{"export:company-vendors"}}]
(let [client (:db/id (dc/pull (dc/db conn) [:db/id] [:client/code (get query-params "client")]))
(let [client (:db/id (dc/pull (dc/db conn) [:db/id] [:client/code (get query-params "client")]))
_ (assert-can-see-client identity client)
data (->> (dc/q '[:find (pull ?v [:vendor/name
:vendor/terms
{:vendor/default-account [:account/name :account/numeric-code
{:account/client-overrides
[:account-client-override/client
:account-client-override/name]}]
:vendor/terms-overrides [:vendor-terms-override/client
:vendor-terms-override/terms]
:vendor/account-overrides [:vendor-account-override/client
{:vendor-account-override/account [:account/numeric-code :account/name
{:account/client-overrides
[:account-client-override/client
:account-client-override/name]}]}]
:vendor/address [:address/street1 :address/city :address/state :address/zip]}])
:in $ ?c
:where [?vu :vendor-usage/client ?c]
[?vu :vendor-usage/count ?count]
[(>= ?vu 0)]
[?vu :vendor-usage/vendor ?v]
(not [?v :vendor/hidden true])]
(dc/db conn)
client)
(map (fn [[v]]
[(-> v :vendor/name)
(-> v :vendor/address :address/street1)
(-> v :vendor/address :address/city)
(-> v :vendor/address :address/state)
(-> v :vendor/address :address/zip)
(-> v (vendor/terms-for-client-id client) )
(-> v (vendor/account-for-client-id client) (accounts/clientize client) :account/name)
(-> v (vendor/account-for-client-id client) :account/numeric-code)
]
))
(into [["Vendor Name" "Address" "City" "State" "Zip" "Terms" "Account" "Account Code"]]))]
{:body
(into []
data)
:headers {"content-disposition" "attachment; filename=\"vendors.csv\""}})))
_ (assert-can-see-client identity client)
data (->> (dc/q '[:find (pull ?v [:vendor/name
:vendor/terms
{:vendor/default-account [:account/name :account/numeric-code
{:account/client-overrides
[:account-client-override/client
:account-client-override/name]}]
:vendor/terms-overrides [:vendor-terms-override/client
:vendor-terms-override/terms]
:vendor/account-overrides [:vendor-account-override/client
{:vendor-account-override/account [:account/numeric-code :account/name
{:account/client-overrides
[:account-client-override/client
:account-client-override/name]}]}]
:vendor/address [:address/street1 :address/city :address/state :address/zip]}])
:in $ ?c
:where [?vu :vendor-usage/client ?c]
[?vu :vendor-usage/count ?count]
[(>= ?vu 0)]
[?vu :vendor-usage/vendor ?v]
(not [?v :vendor/hidden true])]
(dc/db conn)
client)
(map (fn [[v]]
[(-> v :vendor/name)
(-> v :vendor/address :address/street1)
(-> v :vendor/address :address/city)
(-> v :vendor/address :address/state)
(-> v :vendor/address :address/zip)
(-> v (vendor/terms-for-client-id client))
(-> v (vendor/account-for-client-id client) (accounts/clientize client) :account/name)
(-> v (vendor/account-for-client-id client) :account/numeric-code)]))
(into [["Vendor Name" "Address" "City" "State" "Zip" "Terms" "Account" "Account Code"]]))]
{:body
(into []
data)
:headers {"content-disposition" "attachment; filename=\"vendors.csv\""}})))
(defn export-ledger [{:keys [identity query-params]}]
(let [start-date (or (some-> (query-params "start-date")
@@ -282,139 +275,138 @@
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:ledger2"}}]
(let [results (->> (dc/q '[:find (pull ?e [:db/id
:journal-entry/external-id
:journal-entry/cleared
:journal-entry/alternate-description
:journal-entry/date
:journal-entry/note
:journal-entry/amount
:journal-entry/source
:journal-entry/cleared-against
:journal-entry/original-entity
{:journal-entry/client [:client/name :client/code :db/id]
:journal-entry/vendor [:vendor/name :db/id]
:journal-entry/line-items
[:db/id
:journal-entry-line/location
:journal-entry-line/debit
:journal-entry-line/credit
{:journal-entry-line/account
[:bank-account/include-in-reports
:bank-account/bank-name
:bank-account/numeric-code
:bank-account/code
:bank-account/visible
:bank-account/name
:bank-account/number
:account/code
:account/name
:account/numeric-code
:account/location
{:account/type [:db/ident :db/id]}
{:bank-account/type [:db/ident :db/id]}]}]}])
:in $ ?c ?start-date
:where [?e :journal-entry/client ?c]
[?e :journal-entry/date ?date]
[(>= ?date ?start-date)]]
(dc/db conn)
[:client/code (query-params "client-code")]
(coerce/to-date start-date)))
tf-result (transduce (comp
(map first)
(filter (fn [je]
(every?
(fn [jel]
(let [include-in-reports (-> jel :journal-entry-line/account :bank-account/include-in-reports)]
(or (nil? include-in-reports)
(true? include-in-reports))))
(:journal-entry/line-items je))))
(map <-graphql))
conj
(list)
results)]
{:body
tf-result}))))
(let [results (->> (dc/q '[:find (pull ?e [:db/id
:journal-entry/external-id
:journal-entry/cleared
:journal-entry/alternate-description
:journal-entry/date
:journal-entry/note
:journal-entry/amount
:journal-entry/source
:journal-entry/cleared-against
:journal-entry/original-entity
{:journal-entry/client [:client/name :client/code :db/id]
:journal-entry/vendor [:vendor/name :db/id]
:journal-entry/line-items
[:db/id
:journal-entry-line/location
:journal-entry-line/debit
:journal-entry-line/credit
{:journal-entry-line/account
[:bank-account/include-in-reports
:bank-account/bank-name
:bank-account/numeric-code
:bank-account/code
:bank-account/visible
:bank-account/name
:bank-account/number
:account/code
:account/name
:account/numeric-code
:account/location
{:account/type [:db/ident :db/id]}
{:bank-account/type [:db/ident :db/id]}]}]}])
:in $ ?c ?start-date
:where [?e :journal-entry/client ?c]
[?e :journal-entry/date ?date]
[(>= ?date ?start-date)]]
(dc/db conn)
[:client/code (query-params "client-code")]
(coerce/to-date start-date)))
tf-result (transduce (comp
(map first)
(filter (fn [je]
(every?
(fn [jel]
(let [include-in-reports (-> jel :journal-entry-line/account :bank-account/include-in-reports)]
(or (nil? include-in-reports)
(true? include-in-reports))))
(:journal-entry/line-items je))))
(map <-graphql))
conj
(list)
results)]
{:body
tf-result}))))
(defn export-accounts [{:keys [query-params identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:accounts"}}]
(let [client-id (d-clients/code->id (query-params "client-code"))
query [[:all-accounts
[:id :numeric_code :type :applicability :location :name [:client_overrides [:name [:client [:id :code :name]]]]]]]
all-accounts (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))]
(let [client-id (d-clients/code->id (query-params "client-code"))
query [[:all-accounts
[:id :numeric_code :type :applicability :location :name [:client_overrides [:name [:client [:id :code :name]]]]]]]
all-accounts (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))]
{:body
(list (transduce
(comp
(filter (fn [a]
(let [overriden-clients (set (map (comp :id :client) (:client-overrides a)))]
(or (nil? (:applicability a))
(= :global (:applicability a))
(overriden-clients (str client-id))))))
(map (fn [a]
(let [client->name (reduce
(fn [override co]
(assoc override (str (:id (:client co))) (:name co)))
{}
(:client-overrides a))]
(-> a
(assoc :global-name (:name a))
(assoc :client-name (client->name (str client-id) (:name a)))
(dissoc :client-overrides))))))
conj
(list)
(:all-accounts (:data all-accounts))))})))
{:body
(list (transduce
(comp
(filter (fn [a]
(let [overriden-clients (set (map (comp :id :client) (:client-overrides a)))]
(or (nil? (:applicability a))
(= :global (:applicability a))
(overriden-clients (str client-id))))))
(map (fn [a]
(let [client->name (reduce
(fn [override co]
(assoc override (str (:id (:client co))) (:name co)))
{}
(:client-overrides a))]
(-> a
(assoc :global-name (:name a))
(assoc :client-name (client->name (str client-id) (:name a)))
(dissoc :client-overrides))))))
conj
(list)
(:all-accounts (:data all-accounts))))})))
(defn export-transactions2 [{:keys [query-params identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:transactions2"}}]
{:body (let [db (dc/db conn)]
(->>
(dc/q {:find ['?e]
:in ['$ '?client-code]
:where ['[?e :transaction/client ?client-code]]}
db [:client/code (query-params "client-code")])
(map first)
{:body (let [db (dc/db conn)]
(->>
(dc/q {:find ['?e]
:in ['$ '?client-code]
:where ['[?e :transaction/client ?client-code]]}
db [:client/code (query-params "client-code")])
(map first)
;; TODO
#_(map (fn [e]
(let [e (dc/entity db e)
client (:transaction/client e)
bank-account (:transaction/bank-account e)]
{:id (:db/id e)
:date (:transaction/date e)
:post_date (:transaction/post-date e)
:client { :code (:client/code client)
:id (:db/id client)
:name (:client/name client)}
:amount (:transaction/amount e)
:description_original (:transaction/description-original e)
:approval_status (:transaction/approval-status e)
:bank_account {:name (:bank-account/name bank-account)
:code (:bank-account/code bank-account)
:id (:db/id bank-account)}})))))}))
#_(map (fn [e]
(let [e (dc/entity db e)
client (:transaction/client e)
bank-account (:transaction/bank-account e)]
{:id (:db/id e)
:date (:transaction/date e)
:post_date (:transaction/post-date e)
:client {:code (:client/code client)
:id (:db/id client)
:name (:client/name client)}
:amount (:transaction/amount e)
:description_original (:transaction/description-original e)
:approval_status (:transaction/approval-status e)
:bank_account {:name (:bank-account/name bank-account)
:code (:bank-account/code bank-account)
:id (:db/id bank-account)}})))))}))
(defn export-transactions [{:keys [query-params identity clients]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:transactions"}}]
(let [[transactions] (d-transactions/get-graphql {:client-code (query-params "client-code")
:clients clients
#_#_:original-id (Integer/parseInt (query-params "original"))
:count Integer/MAX_VALUE})]
{:body (map
(comp ->graphql
(fn [i]
(cond-> i
true (update :transaction/date to-date)
true (update :transaction/post-date to-date)
(:transaction/payment i) (update-in [:transaction/payment :payment/date] to-date)
(:transaction/expected-deposit i) (update-in [:transaction/expected-deposit :expected-deposit/date] to-date))))
transactions)})))
(let [[transactions] (d-transactions/get-graphql {:client-code (query-params "client-code")
:clients clients
#_#_:original-id (Integer/parseInt (query-params "original"))
:count Integer/MAX_VALUE})]
{:body (map
(comp ->graphql
(fn [i]
(cond-> i
true (update :transaction/date to-date)
true (update :transaction/post-date to-date)
(:transaction/payment i) (update-in [:transaction/payment :payment/date] to-date)
(:transaction/expected-deposit i) (update-in [:transaction/expected-deposit :expected-deposit/date] to-date))))
transactions)})))
(defn export-trial-balance [{{:strs [client-code as-of]} :query-params}]
(let [db (dc/db conn)
@@ -425,44 +417,44 @@
as-of (coerce/to-date (atime/parse as-of atime/iso-date))]
{:body
(->>
(dc/index-pull db {:index :avet
:selector [:db/id :journal-entry-line/debit :journal-entry-line/location :journal-entry-line/credit {:journal-entry-line/account [:db/id {:account/type [:db/ident]}]} :journal-entry-line/client+account+location+date]
:start [:journal-entry-line/client+account+location+date
[client]]})
(take-while (fn [jel]
(let [[c _ _ _] (:journal-entry-line/client+account+location+date jel)]
(= c client))))
(filter (fn [jel]
(let [[_ _ _ d] (:journal-entry-line/client+account+location+date jel)]
(<= (compare (or d #inst "2000-01-01") as-of) 0))))
(reduce
(fn [acc jel]
(update acc [(:db/id (:journal-entry-line/account jel)) (:journal-entry-line/location jel)]
(fn [v]
(if (#{:account-type/asset
:account-type/dividend
:account-type/expense} (:db/ident (:account/type (:journal-entry-line/account jel))))
(update (or v {}) :debit (fnil + 0.0) (or (:journal-entry-line/debit jel) 0.0))
(update (or v {}) :credit (fnil + 0.0) (or (:journal-entry-line/credit jel) 0.0))))))
{})
(map (fn [[[a l] {:keys [debit credit]}]]
[(or (pull-attr db :account/name a)
(pull-attr db :bank-account/name a))
(or (pull-attr db :account/numeric-code a)
(pull-attr db :bank-account/numeric-code a))
l
(or debit 0.0)
(or credit 0.0)]))
(sort-by second))
(->>
(dc/index-pull db {:index :avet
:selector [:db/id :journal-entry-line/debit :journal-entry-line/location :journal-entry-line/credit {:journal-entry-line/account [:db/id {:account/type [:db/ident]}]} :journal-entry-line/client+account+location+date]
:start [:journal-entry-line/client+account+location+date
[client]]})
(take-while (fn [jel]
(let [[c _ _ _] (:journal-entry-line/client+account+location+date jel)]
(= c client))))
(filter (fn [jel]
(let [[_ _ _ d] (:journal-entry-line/client+account+location+date jel)]
(<= (compare (or d #inst "2000-01-01") as-of) 0))))
(reduce
(fn [acc jel]
(update acc [(:db/id (:journal-entry-line/account jel)) (:journal-entry-line/location jel)]
(fn [v]
(if (#{:account-type/asset
:account-type/dividend
:account-type/expense} (:db/ident (:account/type (:journal-entry-line/account jel))))
(update (or v {}) :debit (fnil + 0.0) (or (:journal-entry-line/debit jel) 0.0))
(update (or v {}) :credit (fnil + 0.0) (or (:journal-entry-line/credit jel) 0.0))))))
{})
(map (fn [[[a l] {:keys [debit credit]}]]
[(or (pull-attr db :account/name a)
(pull-attr db :bank-account/name a))
(or (pull-attr db :account/numeric-code a)
(pull-attr db :bank-account/numeric-code a))
l
(or debit 0.0)
(or credit 0.0)]))
(sort-by second))
:status 200}))
(defn export-raw [{:keys [query-params identity]}]
(assert-admin identity)
(alog/info ::executing-query :q (get query-params "query" ))
(alog/info ::executing-query :q (get query-params "query"))
(statsd/time! [(str "export.time") {:tags #{"export:raw"}}]
{:body
(into (list) (apply dc/q (read-string (get query-params "query" )) (into [(dc/db conn)] (read-string (get query-params "args" "[]")))))}))
{:body
(into (list) (apply dc/q (read-string (get query-params "query")) (into [(dc/db conn)] (read-string (get query-params "args" "[]")))))}))
(defn export-ntg-account-snapshot [_]
(let [clients (->> (dc/q '[:find (pull ?e [:db/id :client/code :client/locations])
@@ -559,65 +551,58 @@
:sales-order/service-charge
{:sales-order/charges [:charge/total :charge/tax :charge/tip
:charge/type-name
:charge/reference-link
:charge/type-name
:charge/reference-link
{[:charge/processor :xform iol-ion.query/ident] [:db/ident]}]
:sales-order/line-items [:order-line-item/item-name
:order-line-item/category
:order-line-item/total]}
]
:order-line-item/total]}]
:start [:sales-order/client+date [(:db/id client) (coerce/to-date date)]]
:end [:sales-order/client+date [(:db/id client) (coerce/to-date end)]]
:reverse false
:limit 100})
(take-while (fn matches-client [curr]
(and
(= (first (:sales-order/client+date curr))
(:db/id client))
(< (compare (:sales-order/date curr)
(coerce/to-date end))
0))
)))
]
(and
(= (first (:sales-order/client+date curr))
(:db/id client))
(< (compare (:sales-order/date curr)
(coerce/to-date end))
0)))))]
entry all-entries
:let [sales-columns [(-> entry :sales-order/client :client/name)
(atime/unparse-local (coerce/from-date (-> entry :sales-order/date)) atime/standard-time)
(-> entry :sales-order/total)
(-> entry :sales-order/tip)
(-> entry :sales-order/service-charge)
(-> entry :sales-order/reference-link)
]
sales-column-count (count sales-columns)
tender-column-count 6]
(atime/unparse-local (coerce/from-date (-> entry :sales-order/date)) atime/standard-time)
(-> entry :sales-order/total)
(-> entry :sales-order/tip)
(-> entry :sales-order/service-charge)
(-> entry :sales-order/reference-link)]
sales-column-count (count sales-columns)
tender-column-count 6]
row (concat [sales-columns]
(map (fn [tender]
(concat
(take sales-column-count (repeat nil))
[
(-> tender :charge/total)
(-> tender :charge/tax)
(-> tender :charge/tip)
(-> tender :charge/type-name)
(some-> tender :charge/processor name)
(-> tender :charge/reference-link)
]))
(:sales-order/charges entry))
(map (fn [tender]
(concat
(take (+ sales-column-count tender-column-count) (repeat nil))
[
(-> tender :order-line-item/item-name)
(-> tender :order-line-item/category)
(-> tender :order-line-item/total)
]))
(:sales-order/line-items entry)))]
(map (fn [tender]
(concat
(take sales-column-count (repeat nil))
[(-> tender :charge/total)
(-> tender :charge/tax)
(-> tender :charge/tip)
(-> tender :charge/type-name)
(some-> tender :charge/processor name)
(-> tender :charge/reference-link)]))
(:sales-order/charges entry))
(map (fn [tender]
(concat
(take (+ sales-column-count tender-column-count) (repeat nil))
[(-> tender :order-line-item/item-name)
(-> tender :order-line-item/category)
(-> tender :order-line-item/total)]))
(:sales-order/line-items entry)))]
row
#_(let [balance (account-lookup (format "%d-%d-%s-%s" (:db/id client) numeric-code l date-str))
_ (when balance
(reset! last-used-value balance))
balance (or balance @last-used-value)]
[ l numeric-code (account->name a) date-str
[l numeric-code (account->name a) date-str
(format "%.2f" balance)])))}))
#_(export-ntg-payment-snapshot nil)
@@ -628,7 +613,6 @@
(handler request)
{:status 401})))
(def routes2 {"api/" {"sales/" {"aggregated/" {#"export/?" {:get :aggregated-sales-export}}
#"export/?" {:get :export-sales}
"ntg-export" {:get :export-ntg-sales-snapshot}}
@@ -658,12 +642,11 @@
:export-ledger (-> export-ledger wrap-json-response wrap-secure)
:export-ntg-account-snapshot (-> export-ntg-account-snapshot wrap-csv-response (wrap-predetermined-api-key "fd07755a-ed4c-4c9a-ad85-fbdd8af37206"))
:export-ntg-sales-snapshot (-> export-ntg-sales-snapshot wrap-csv-response
(wrap-schema-enforce :query-schema (mc/schema [:map
[:date {:required true
:decode/string #(try (atime/parse % atime/iso-date) (catch Exception _ nil))} :some]]) )
(wrap-form-4xx-2 (fn [_] {:body "Invalid Date"}))
(wrap-predetermined-api-key "fd07755a-ed4c-4c9a-ad85-fbdd8af37206")
)
(wrap-schema-enforce :query-schema (mc/schema [:map
[:date {:required true
:decode/string #(try (atime/parse % atime/iso-date) (catch Exception _ nil))} :some]]))
(wrap-form-4xx-2 (fn [_] {:body "Invalid Date"}))
(wrap-predetermined-api-key "fd07755a-ed4c-4c9a-ad85-fbdd8af37206"))
:export-trial-balance (-> export-trial-balance wrap-csv-response wrap-secure)
:export-accounts (-> export-accounts wrap-json-response wrap-secure)
:export-transactions (-> export-transactions wrap-json-response wrap-secure)

View File

@@ -7,12 +7,12 @@
(defn handle-ezcater [{:keys [request-method json-params] :as r}]
(cond
(= :get request-method)
{:status 200
{:status 200
:headers {"Content-Type" "application/json"}
:body "{}"}
(= :post request-method)
(do
(do
(alog/info ::ezcater-request
:json-params json-params)
(e/import-order json-params)
@@ -23,8 +23,6 @@
:else
{:status 404}))
(def routes {"api/" {"ezcater/" {#"event/?" :ezcater-event}}})
(def match->handler {:ezcater-event (-> handle-ezcater
wrap-json-params)})
wrap-json-params)})

View File

@@ -53,54 +53,54 @@
event-date (some-> (excel/xls-date->date event-date)
coerce/to-date-time
atime/as-local-time
coerce/to-date )]
(cond (and event-date client-id location )
coerce/to-date)]
(cond (and event-date client-id location)
[:order #:sales-order
{:date event-date
:external-id (str "ezcater/order/" client-id "-" location "-" order-number)
:client client-id
:location location
:reference-link (str order-number)
:line-items [#:order-line-item
{:external-id (str "ezcater/order/" client-id "-" location "-" order-number "-" 0)
:item-name "EZCater Catering"
:category "EZCater Catering"
:discount (fmt-amount (or adjustments 0.0))
:tax (fmt-amount tax)
:total (fmt-amount (+ food-total
tax))}]
{:date event-date
:external-id (str "ezcater/order/" client-id "-" location "-" order-number)
:client client-id
:location location
:reference-link (str order-number)
:line-items [#:order-line-item
{:external-id (str "ezcater/order/" client-id "-" location "-" order-number "-" 0)
:item-name "EZCater Catering"
:category "EZCater Catering"
:discount (fmt-amount (or adjustments 0.0))
:tax (fmt-amount tax)
:total (fmt-amount (+ food-total
tax))}]
:charges [#:charge
{:type-name "CARD"
:date event-date
:client client-id
:location location
:external-id (str "ezcater/charge/" client-id "-" location "-" order-number "-" 0)
:processor :ccp-processor/ezcater
:total (fmt-amount (+ food-total
tax
tip))
:tip (fmt-amount tip)}]
:total (fmt-amount (+ food-total
tax
(or adjustments 0.0)))
:discount (fmt-amount (or adjustments 0.0))
:service-charge (fmt-amount (+ fee commission))
:tax (fmt-amount tax)
:tip (fmt-amount tip)
:returns 0.0
:vendor :vendor/ccp-ezcater}]
:charges [#:charge
{:type-name "CARD"
:date event-date
:client client-id
:location location
:external-id (str "ezcater/charge/" client-id "-" location "-" order-number "-" 0)
:processor :ccp-processor/ezcater
:total (fmt-amount (+ food-total
tax
tip))
:tip (fmt-amount tip)}]
:total (fmt-amount (+ food-total
tax
(or adjustments 0.0)))
:discount (fmt-amount (or adjustments 0.0))
:service-charge (fmt-amount (+ fee commission))
:tax (fmt-amount tax)
:tip (fmt-amount tip)
:returns 0.0
:vendor :vendor/ccp-ezcater}]
caterer-name
(do
(alog/warn ::missing-client
:order order-number
:store-name store-name
:caterer-name caterer-name)
[:missing caterer-name])
caterer-name
(do
(alog/warn ::missing-client
:order order-number
:store-name store-name
:caterer-name caterer-name)
[:missing caterer-name])
:else
nil)))
:else
nil)))
(defn stream->sales-orders [s]
(let [clients (map first (dc/q '[:find (pull ?c [:client/code
@@ -115,7 +115,7 @@
object (str "/ezcater-xls/" (str (java.util.UUID/randomUUID)))]
(mu/log ::writing-temp-xls
:location object)
(s3/put-object {:bucket-name (:data-bucket env)
(s3/put-object {:bucket-name (:data-bucket env)
:key object
:input-stream s})
(into []
@@ -157,13 +157,13 @@
});")]])])
(defn upload-xls [{:keys [identity] :as request}]
(let [file (or (get (:params request) :file)
(get (:params request) "file"))]
(mu/log ::uploading-file
:file file)
(with-open [s (io/input-stream (:tempfile file))]
(try
(try
(let [parse-results (stream->sales-orders s)
new-orders (->> parse-results
(filter (comp #{:order} first))
@@ -181,7 +181,7 @@
[:li ml])]])]))
(catch Exception e
(alog/error ::import-error
:error e)
:error e)
(html-response [:div (.getMessage e)]))))))
(defn page [{:keys [matched-route request-method] :as request}]

View File

@@ -11,9 +11,6 @@
[clojure.set :as set]
[datomic.api :as dc]))
(defn handle-graphql [{:keys [request-method query-params clients] :as r}]
(when (= "none" (:user/role (:identity r)))
(throw-unauthorized))
@@ -22,21 +19,21 @@
(let [variables (some-> (query-params "variables")
edn/read-string)
body (some-> r :body slurp)]
{:status 200
:body (pr-str (ql/query (:identity r) (if (= request-method :get) (query-params "query") body) (assoc variables
:clients
clients) ))
clients)))
:headers {"Content-Type" "application/edn"}})
(catch Throwable e
(if-let [result (:result (ex-data e))]
(do (alog/warn ::result-error :error e)
{:status 400
:body (pr-str result)
:headers {"Content-Type" "application/edn"}})
(if-let [message (:validation-error (ex-data (.getCause e)) )]
(do
{:status 400
:body (pr-str result)
:headers {"Content-Type" "application/edn"}})
(if-let [message (:validation-error (ex-data (.getCause e)))]
(do
(alog/warn ::graphql-validation-error
:message message
:error e)
@@ -48,6 +45,5 @@
:body (pr-str {:errors [{:message (str "Unhandled error:" (str e))}]})
:headers {"Content-Type" "application/edn"}}))))))
(def routes {"api/" {#"graphql/?" :graphql}})
(def match->handler {:graphql (wrap-secure handle-graphql)})

View File

@@ -29,10 +29,10 @@
{:vendor-code vendor-code})))
(let [vendor-id (or forced-vendor
(->> (dc/q
{:find ['?vendor]
:in ['$ '?vendor-name]
:where ['[?vendor :vendor/name ?vendor-name]]}
(dc/db conn) vendor-code)
{:find ['?vendor]
:in ['$ '?vendor-name]
:where ['[?vendor :vendor/name ?vendor-name]]}
(dc/db conn) vendor-code)
first
first))]
(when-not vendor-id
@@ -40,9 +40,9 @@
{:vendor-code vendor-code})))
(if-let [matching-vendor (->> (dc/q
{:find [(list 'pull '?vendor-id d-vendors/default-read)]
:in ['$ '?vendor-id]}
(dc/db conn) vendor-id)
{:find [(list 'pull '?vendor-id d-vendors/default-read)]
:in ['$ '?vendor-id]}
(dc/db conn) vendor-id)
first
first)]
matching-vendor
@@ -54,16 +54,16 @@
account-number (:db/id (d-clients/exact-match account-number))
customer-identifier (:db/id (d-clients/best-match customer-identifier))
client-override (Long/parseLong client-override))
_ (alog/info ::client-matched
:account-number account-number
_ (alog/info ::client-matched
:account-number account-number
:customer-identifier customer-identifier
:client-override client-override
:matching (when matching-client
(dc/pull (dc/db conn) [:client/name :client/code] matching-client)))
:client-override client-override
:matching (when matching-client
(dc/pull (dc/db conn) [:client/name :client/code] matching-client)))
matching-vendor (match-vendor vendor-code vendor-override)
matching-location (or (when-not (str/blank? location-override)
location-override)
location-override)
(parse/best-location-match (dc/pull (dc/db conn)
[{:client/location-matches [:location-match/location :location-match/matches]}
:client/default-location
@@ -97,7 +97,6 @@
invoice)
(defn admin-only-if-multiple-clients [is]
(let [client-count (->> is
(map :invoice/client)
@@ -112,8 +111,8 @@
(map #(validate-invoice % user))
admin-only-if-multiple-clients
(mapv d-invoices/code-invoice)
(mapv (fn [i] [:propose-invoice i])))]
(mapv (fn [i] [:propose-invoice i])))]
(alog/info ::creating-invoice :invoices potential-invoices)
(let [tx (audit-transact potential-invoices user)]
(when-not (seq (dc/q '[:find ?i
@@ -155,7 +154,7 @@
customer)))
code->existing-account (by :account/numeric-code (map first (dc/q {:find ['(pull ?e [:account/numeric-code
{:account/applicability [:db/ident]}
:db/id])]
:db/id])]
:in ['$]
:where ['[?e :account/name]]}
(dc/db conn))))
@@ -225,15 +224,15 @@
(defn import-transactions-cleared-against [file]
(let [[_ & rows] (-> file (io/reader) csv/read-csv)
txes (transduce
(comp
(filter (fn [[transaction-id _]]
(dc/pull (dc/db conn) '[:transaction/amount] (Long/parseLong transaction-id))))
(map (fn [[transaction-id cleared-against]]
{:db/id (Long/parseLong transaction-id)
:transaction/cleared-against cleared-against})))
conj
[]
rows)]
(comp
(filter (fn [[transaction-id _]]
(dc/pull (dc/db conn) '[:transaction/amount] (Long/parseLong transaction-id))))
(map (fn [[transaction-id cleared-against]]
{:db/id (Long/parseLong transaction-id)
:transaction/cleared-against cleared-against})))
conj
[]
rows)]
(audit-transact txes nil)))
(defn batch-upload-transactions [{{:keys [data]} :edn-params user :identity}]
@@ -318,8 +317,6 @@
:data (ex-data e)})
:headers {"Content-Type" "application/edn"}}))))
(defn bulk-account-overrides [{{files :file
files-2 "file"
client :client

View File

@@ -25,8 +25,6 @@
(csv/write-csv w %)
(.toString w))))))
(defn execute-query [query-params params]
(let [{:keys [query-id]} params]
(mu/with-context {:query-id query-id}
@@ -37,7 +35,6 @@
(into (list) (apply dc/q (edn/read-string query-string)
(into [(dc/db conn)] (edn/read-string (get query-params "args" "[]")))))))))
(defn put-query [guid body note & [lookup-key client]]
(let [id (pull-id (dc/db conn) [:saved-query/lookup-key lookup-key])
guid (if lookup-key
@@ -45,11 +42,11 @@
guid)
guid)]
@(dc/transact conn [[:upsert-entity {:db/id (or id (random-tempid))
:saved-query/guid guid
:saved-query/description note
:saved-query/key (str "queries/" guid)
:saved-query/client client
:saved-query/lookup-key lookup-key}]])
:saved-query/guid guid
:saved-query/description note
:saved-query/key (str "queries/" guid)
:saved-query/client client
:saved-query/lookup-key lookup-key}]])
(s3/put-object :bucket-name (:data-bucket env)
:key (str "queries/" guid)
:input-stream (io/make-input-stream (.getBytes body) {})
@@ -62,8 +59,6 @@
:csv-results-url (str "/api/queries/" guid "/results/csv")
:json-results-url (str "/api/queries/" guid "/results/json")}}))
(defn get-queries [{:keys [identity]}]
(assert-admin identity)
(let [obj (s3/list-objects :bucket-name (:data-bucket env)
@@ -77,7 +72,7 @@
(assert-admin identity)
(put-query (str (UUID/randomUUID)) (body-string request) (query-params "note")))
(defn get-query [{:keys [identity params]} ]
(defn get-query [{:keys [identity params]}]
(assert-admin identity)
(let [{:keys [query-id]} params
obj (s3/get-object :bucket-name (:data-bucket env)
@@ -89,13 +84,13 @@
:csv-results-url (str "/api/queries/" query-id "/results/csv")
:json-results-url (str "/api/queries/" query-id "/results/json")}}))
(defn update-query [{:keys [query-params identity params] :as request} ]
(defn update-query [{:keys [query-params identity params] :as request}]
(assert-admin identity)
(put-query (:query-id params) (body-string request) (query-params "note")))
(defn results-json-query [{:keys [query-params params]}]
(defn results-json-query [{:keys [query-params params]}]
(statsd/time! [(str "export.query.time") {:tags #{(str "query:" (:query-id params))}}]
{:body (execute-query query-params params)}))
{:body (execute-query query-params params)}))
(defn raw-query [{:keys [identity params]}]
(assert-admin identity)
@@ -124,9 +119,9 @@
:create-query create-query
:raw-query raw-query
:get-query (-> get-query
wrap-json-response)
wrap-json-response)
:update-query (-> update-query
wrap-json-response)
wrap-json-response)
:results-json-query (-> results-json-query
wrap-json-response)

View File

@@ -11,7 +11,7 @@
(defn fastlink [{:keys [query-params identity]}]
(assert-can-see-client identity (pull-attr (dc/db conn) :db/id [:client/code (get query-params "client")]))
(let [token (if-let [client-id (get query-params "client-id")]
(-> client-id
Long/parseLong
@@ -19,22 +19,22 @@
:client/code
(yodlee/get-access-token))
(yodlee/get-access-token (get query-params "client")))]
{:status 200
{:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str {:token token
:url (:yodlee2-fastlink env)}) }))
:url (:yodlee2-fastlink env)})}))
(defn refresh-provider-accounts [{:keys [identity edn-params]}]
(assert-admin identity)
(alog/info ::refreshing :params edn-params)
(try
(try
(yodlee/refresh-provider-account (-> (:client-id edn-params)
Long/parseLong
d-clients/get-by-id
:client/code)
(:provider-account-id edn-params))
{:status 200
{:status 200
:headers {"Content-Type" "application/edn"}
:body "{}" }
:body "{}"}
(catch Exception e
(alog/error ::error :error e)
{:status 400
@@ -48,9 +48,9 @@
(alog/info ::looking-up
:client client
:id id)
(try
{:status 200
(try
{:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str (yodlee/get-provider-account-detail (-> client
Long/parseLong
@@ -70,12 +70,12 @@
{:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str (yodlee/reauthenticate-and-recache
(-> (:client-id data)
Long/parseLong
d-clients/get-by-id
:client/code)
(Long/parseLong id)
(dissoc data :client-id )))}
(-> (:client-id data)
Long/parseLong
d-clients/get-by-id
:client/code)
(Long/parseLong id)
(dissoc data :client-id)))}
(catch Exception e
(alog/error ::error :error e)
{:status 500
@@ -85,15 +85,15 @@
(defn delete-provider-account [{:keys [edn-params identity]}]
(assert-admin identity)
(try
(try
(yodlee/delete-provider-account (-> (:client-id edn-params)
Long/parseLong
d-clients/get-by-id
:client/code)
(:provider-account-id edn-params))
{:status 200
{:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str {}) }
:body (pr-str {})}
(catch Exception e
(alog/error ::error :error e)
{:status 400
@@ -110,11 +110,11 @@
(def routes {"api" {"/yodlee2" {"/fastlink" :fastlink
"/provider-accounts/refresh/" :refresh-provider-accounts
["/provider-accounts/" :client "/" :id ] :get-provider-account-detail
["/reauthenticate/" :id ] :reauthenticate
["/provider-accounts/" :client "/" :id] :get-provider-account-detail
["/reauthenticate/" :id] :reauthenticate
"/provider-accounts/delete/" :delete-provider-account}}})
(def match->handler {:fastlink (-> fastlink wrap-secure (valid-for :get))
:refresh-provider-accounts (-> refresh-provider-accounts wrap-secure (valid-for :post))
:get-provider-account-detail (-> get-provider-account-detail wrap-secure (valid-for :get))
:reauthenticate (-> reauthenticate wrap-secure (valid-for :post))
:delete-provider-account (-> delete-provider-account wrap-secure (valid-for :post))} )
:delete-provider-account (-> delete-provider-account wrap-secure (valid-for :post))})