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:
@@ -18,7 +18,6 @@
|
||||
[iol-ion.tx :refer [random-tempid]]
|
||||
[com.brunobonacci.mulog :as mu]))
|
||||
|
||||
|
||||
(defn get-all-graphql [context args _]
|
||||
(assert-admin (:id context))
|
||||
(let [args (assoc args :id (:id context))
|
||||
|
||||
@@ -97,7 +97,6 @@
|
||||
[:line {:line-width 0.15 :color [50 50 50]}]]
|
||||
[:cell {:colspan 3}]]
|
||||
|
||||
|
||||
[[:cell {:size 9 :leading 11.5} "\n\n\n\n\nMEMO"]
|
||||
[:cell {:colspan 5 :leading 11.5} (split-memo memo)
|
||||
[:line {:line-width 0.15 :color [50 50 50]}]]
|
||||
@@ -186,8 +185,6 @@
|
||||
:payment/pdf-data
|
||||
(edn/read-string)
|
||||
|
||||
|
||||
|
||||
make-check-pdf)]
|
||||
(s3/put-object :bucket-name (:data-bucket env)
|
||||
:key (:payment/s3-key check)
|
||||
@@ -277,7 +274,6 @@
|
||||
(conj payment)
|
||||
(into (invoice-payments invoices invoice-amounts)))))
|
||||
|
||||
|
||||
(defmethod invoices->entities :payment-type/debit [invoices vendor client bank-account type index invoice-amounts date]
|
||||
(when (<= (->> invoices
|
||||
(map (comp invoice-amounts :db/id))
|
||||
@@ -297,7 +293,6 @@
|
||||
(conj payment)
|
||||
(into (invoice-payments invoices invoice-amounts)))))
|
||||
|
||||
|
||||
(defmethod invoices->entities :payment-type/balance-credit [invoices invoice-amounts]
|
||||
(when (<= (->> invoices
|
||||
(map (comp invoice-amounts :db/id))
|
||||
@@ -488,7 +483,6 @@
|
||||
{:s3-url nil
|
||||
:invoices (d-invoices/get-multi (map :invoice_id (:invoice_payments args)))})))
|
||||
|
||||
|
||||
(defn void-payment [context {id :payment_id} _]
|
||||
(let [check (d-checks/get-by-id id)]
|
||||
(assert (or (= :payment-status/pending (:payment/status check))
|
||||
@@ -549,7 +543,6 @@
|
||||
:invoice-status/unpaid)}]]))))))))
|
||||
id))
|
||||
|
||||
|
||||
(defn void-payments [context args _]
|
||||
(assert-admin (:id context))
|
||||
(let [args (assoc args :clients (:clients context))
|
||||
@@ -607,7 +600,6 @@
|
||||
0.001))
|
||||
invoices)
|
||||
|
||||
|
||||
total-to-pay (reduce + 0 (map :invoice/outstanding-balance invoices-to-be-paid))
|
||||
_ (when (<= total-to-pay 0.001)
|
||||
(assert-failure "You must select invoices that need to be paid."))
|
||||
@@ -637,8 +629,6 @@
|
||||
[total-to-pay []])))
|
||||
(into {}))
|
||||
|
||||
|
||||
|
||||
vendor-id (:db/id (:invoice/vendor (first invoices)))
|
||||
payment {:db/id (str vendor-id)
|
||||
:payment/amount total-to-pay
|
||||
@@ -751,7 +741,6 @@
|
||||
{:enum-value :pending}
|
||||
{:enum-value :cleared}]}})
|
||||
|
||||
|
||||
(def resolvers
|
||||
{:get-potential-payments get-potential-payments
|
||||
:get-payment-page get-payment-page
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
(defn get-admin-client [context {:keys [id]} _]
|
||||
(assert-admin (:id context))
|
||||
(->graphql
|
||||
(-> (d-clients/get-by-id id)
|
||||
(update :client/bank-accounts (fn [bas]
|
||||
(map #(set/rename-keys % {:bank-account/use-date-instead-of-post-date? :use-date-instead-of-post-date}) bas))))))
|
||||
(-> (d-clients/get-by-id id)
|
||||
(update :client/bank-accounts (fn [bas]
|
||||
(map #(set/rename-keys % {:bank-account/use-date-instead-of-post-date? :use-date-instead-of-post-date}) bas))))))
|
||||
|
||||
(defn get-client-page [context args _]
|
||||
(assert-admin (:id context))
|
||||
@@ -29,7 +29,7 @@
|
||||
[clients clients-count] (d-clients/get-graphql-page (assoc (<-graphql (:filters args))
|
||||
:clients (:clients context)))
|
||||
clients (->> clients
|
||||
|
||||
|
||||
(map (fn [c]
|
||||
(update c :client/bank-accounts (fn [bas]
|
||||
(map #(set/rename-keys % {:bank-account/use-date-instead-of-post-date? :use-date-instead-of-post-date}) bas)))))
|
||||
@@ -47,13 +47,6 @@
|
||||
bank-accounts))))))]
|
||||
(result->page clients clients-count :clients (:filters args))))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
(def objects
|
||||
{:location_match
|
||||
{:fields {:location {:type 'String}
|
||||
@@ -102,15 +95,13 @@
|
||||
:yodlee_provider_accounts {:type '(list :yodlee_provider_account)}
|
||||
:plaid_items {:type '(list :plaid_item)}}}
|
||||
|
||||
:client_page
|
||||
:client_page
|
||||
{:fields {:clients {:type '(list :client)}
|
||||
:count {:type 'Int}
|
||||
:total {:type 'Int}
|
||||
:start {:type 'Int}
|
||||
:end {:type 'Int}}}
|
||||
|
||||
|
||||
|
||||
:bank_account
|
||||
{:fields {:id {:type :id}
|
||||
:integration_status {:type :integration_status}
|
||||
@@ -139,9 +130,7 @@
|
||||
:forecasted_transaction {:fields {:identifier {:type 'String}
|
||||
:id {:type :id}
|
||||
:day_of_month {:type 'Int}
|
||||
:amount {:type :money}}}
|
||||
|
||||
})
|
||||
:amount {:type :money}}}})
|
||||
|
||||
(def queries
|
||||
{:client {:type '(list :client)
|
||||
@@ -158,12 +147,12 @@
|
||||
{})
|
||||
|
||||
(def input-objects
|
||||
{ :client_filters
|
||||
{:client_filters
|
||||
{:fields {:code {:type 'String}
|
||||
:name_like {:type 'String}
|
||||
:start {:type 'Int}
|
||||
:per_page {:type 'Int}
|
||||
:sort {:type '(list :sort_item)}}} })
|
||||
:sort {:type '(list :sort_item)}}}})
|
||||
|
||||
(def enums
|
||||
{:bank_account_type {:values [{:enum-value :check}
|
||||
@@ -173,11 +162,10 @@
|
||||
(def resolvers
|
||||
{:get-client get-client
|
||||
:get-admin-client get-admin-client
|
||||
:get-client-page get-client-page })
|
||||
|
||||
:get-client-page get-client-page})
|
||||
|
||||
(defn attach [schema]
|
||||
(->
|
||||
(->
|
||||
(merge-with merge schema
|
||||
{:objects objects
|
||||
:queries queries
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
(defn get-all-expected-deposits [context args _]
|
||||
(assert-admin (:id context))
|
||||
(map
|
||||
(comp ->graphql status->graphql)
|
||||
(comp ->graphql status->graphql)
|
||||
(first (d-expected-deposit/get-graphql (assoc (<-graphql args) :count Integer/MAX_VALUE)))))
|
||||
|
||||
(defn get-expected-deposit-page [context args _]
|
||||
|
||||
@@ -17,15 +17,14 @@
|
||||
{:name name
|
||||
:id id}))
|
||||
|
||||
|
||||
(def objects
|
||||
{:ezcater_caterer {:fields {:name {:type 'String}
|
||||
:id {:type :id}}}})
|
||||
|
||||
(def queries
|
||||
{:search_ezcater_caterer {:type '(list :search_result)
|
||||
:args {:query {:type 'String}}
|
||||
:resolve :search-ezcater-caterer}})
|
||||
:args {:query {:type 'String}}
|
||||
:resolve :search-ezcater-caterer}})
|
||||
|
||||
(def enums
|
||||
{})
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
(merge-query {:query {:find ['?e]
|
||||
:where ['[?e :import-batch/date]]}}))]
|
||||
|
||||
|
||||
(cond->> (query2 query)
|
||||
true (apply-sort-3 args)
|
||||
true (apply-pagination args))))
|
||||
@@ -66,9 +65,8 @@
|
||||
(map #(update % :import-batch/date coerce/to-date-time)))
|
||||
matching-count :data args)))
|
||||
|
||||
|
||||
(defn attach [schema]
|
||||
(->
|
||||
(->
|
||||
(merge-with merge schema
|
||||
{:objects {:import_batch {:fields {:user_name {:type 'String}
|
||||
:id {:type :id}
|
||||
@@ -83,12 +81,10 @@
|
||||
:count {:type 'Int}
|
||||
:total {:type 'Int}
|
||||
:start {:type 'Int}
|
||||
:end {:type 'Int}}}
|
||||
|
||||
}
|
||||
:end {:type 'Int}}}}
|
||||
:queries {:import_batch_page {:type :import_batch_page
|
||||
:args {:filters {:type :import_batch_filters}}
|
||||
|
||||
|
||||
:resolve :get-import-batch-page}}
|
||||
:mutations {}
|
||||
:input-objects {:import_batch_filters {:fields {:start {:type 'Int}
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
(defn get-intuit-bank-accounts [context _ _]
|
||||
(assert-admin (:id context))
|
||||
(->graphql (map first (dc/q '[:find (pull ?e [*])
|
||||
:in $
|
||||
:where [?e :intuit-bank-account/external-id]]
|
||||
(dc/db conn)))))
|
||||
:in $
|
||||
:where [?e :intuit-bank-account/external-id]]
|
||||
(dc/db conn)))))
|
||||
|
||||
@@ -174,8 +174,6 @@
|
||||
(let [error (str "Expense account total (" expense-account-total ") does not equal invoice total (" total ")")]
|
||||
(throw (ex-info error {:validation-error error}))))))
|
||||
|
||||
|
||||
|
||||
(defn add-invoice [context {{:keys [expense_accounts client_id vendor_id] :as in} :invoice} _]
|
||||
(assert-no-conflicting in)
|
||||
(assert-can-see-client (:id context) client_id)
|
||||
@@ -193,8 +191,6 @@
|
||||
(when-not ((set (map :db/id (:client/bank-accounts (d-clients/get-by-id client-id)))) bank-account-id)
|
||||
(throw (ex-info (str "Bank account does not belong to client") {:validation-error "Bank account does not belong to client."}))))
|
||||
|
||||
|
||||
|
||||
(defn add-and-print-invoice [context {{:keys [total client_id vendor_id] :as in} :invoice bank-account-id :bank_account_id type :type} _]
|
||||
(mu/trace ::validating-invoice [:invoice in]
|
||||
(do
|
||||
@@ -261,7 +257,6 @@
|
||||
|
||||
(-> (d-invoices/get-by-id id) (->graphql (:id context)))))
|
||||
|
||||
|
||||
(defn get-ids-matching-filters [args]
|
||||
(let [ids (some-> args
|
||||
:filters
|
||||
@@ -448,8 +443,6 @@
|
||||
[])]
|
||||
accounts)))
|
||||
|
||||
|
||||
|
||||
(defn bulk-change-invoices [context args _]
|
||||
(assert-admin (:id context))
|
||||
(when-not (:client_id args)
|
||||
|
||||
@@ -38,9 +38,9 @@
|
||||
_ (when (:client_id (:filters args))
|
||||
(assert-can-see-client (:id context) (:client_id (:filters args))))
|
||||
clients (or (and (:client_id (:filters args))
|
||||
[{:db/id (:client_id (:filters args))}])
|
||||
(:clients context))
|
||||
|
||||
[{:db/id (:client_id (:filters args))}])
|
||||
(:clients context))
|
||||
|
||||
[journal-entries journal-entries-count] (l/get-graphql (assoc (<-graphql (:filters args))
|
||||
:clients clients))
|
||||
|
||||
@@ -55,12 +55,10 @@
|
||||
(let [args (assoc args :id (:id context))
|
||||
[journal-entries journal-entries-count] (l/get-graphql (assoc (<-graphql (:filters args))
|
||||
:per-page Integer/MAX_VALUE
|
||||
:clients (:clients context)))
|
||||
:clients (:clients context)))]
|
||||
|
||||
|
||||
]
|
||||
{:csv_content_b64 (Base64/encodeBase64String
|
||||
(.getBytes
|
||||
(.getBytes
|
||||
(with-open [w (java.io.StringWriter.)]
|
||||
(csv/write-csv w
|
||||
(into [["Client" "Vendor" "Date" "Journal Entry" "Journal Entry Line" "Account Code" "Account Name" "Account Type" "Debit" "Credit" "Net"]]
|
||||
@@ -83,22 +81,19 @@
|
||||
(-> li :journal-entry-line/account :bank-account/numeric-code))
|
||||
(or (-> li :journal-entry-line/account :account/name)
|
||||
(-> li :journal-entry-line/account :bank-account/name))
|
||||
(some-> account-type name )
|
||||
(some-> account-type name)
|
||||
(-> li :journal-entry-line/debit)
|
||||
(-> li :journal-entry-line/credit)
|
||||
(if (#{:account-type/asset
|
||||
:account-type/dividend
|
||||
:account-type/expense} account-type)
|
||||
(- (or (-> li :journal-entry-line/debit) 0.0) (or (-> li :journal-entry-line/credit) 0.0))
|
||||
(- (or (-> li :journal-entry-line/credit) 0.0) (or (-> li :journal-entry-line/debit) 0.0)))
|
||||
|
||||
]))
|
||||
(:journal-entry/line-items j))
|
||||
))))
|
||||
(- (or (-> li :journal-entry-line/credit) 0.0) (or (-> li :journal-entry-line/debit) 0.0)))]))
|
||||
|
||||
(:journal-entry/line-items j))))))
|
||||
:quote? (constantly true))
|
||||
(.toString w))))}))
|
||||
|
||||
|
||||
(defn roll-up-until
|
||||
([lookup-account all-ledger-entries end-date]
|
||||
(roll-up-until lookup-account all-ledger-entries end-date nil))
|
||||
@@ -107,57 +102,56 @@
|
||||
(filter (fn [[d]]
|
||||
(if start-date
|
||||
(and
|
||||
(>= (compare d start-date) 0)
|
||||
(<= (compare d end-date) 0))
|
||||
(>= (compare d start-date) 0)
|
||||
(<= (compare d end-date) 0))
|
||||
(<= (compare d end-date) 0))))
|
||||
(reduce
|
||||
(fn [acc [_ _ account location debit credit]]
|
||||
(-> acc
|
||||
(update-in [[location account] :debit] (fnil + 0.0) debit)
|
||||
(update-in [[location account] :credit] (fnil + 0.0) credit)
|
||||
(update-in [[location account] :count] (fnil + 0) 1))
|
||||
)
|
||||
{})
|
||||
(fn [acc [_ _ account location debit credit]]
|
||||
(-> acc
|
||||
(update-in [[location account] :debit] (fnil + 0.0) debit)
|
||||
(update-in [[location account] :credit] (fnil + 0.0) credit)
|
||||
(update-in [[location account] :count] (fnil + 0) 1)))
|
||||
{})
|
||||
(reduce-kv
|
||||
(fn [acc [location account-id] {:keys [debit credit count]}]
|
||||
(let [account (lookup-account account-id)
|
||||
account-type (:account_type account)]
|
||||
|
||||
(conj acc (merge {:id (str account-id "-" location)
|
||||
:location (or location "")
|
||||
:count count
|
||||
:debits debit
|
||||
:credits credit
|
||||
:amount (if account-type (if (#{:account-type/asset
|
||||
:account-type/dividend
|
||||
:account-type/expense} account-type)
|
||||
(- debit credit)
|
||||
(- credit debit))
|
||||
0.0)}
|
||||
account))))
|
||||
[]))))
|
||||
(fn [acc [location account-id] {:keys [debit credit count]}]
|
||||
(let [account (lookup-account account-id)
|
||||
account-type (:account_type account)]
|
||||
|
||||
(conj acc (merge {:id (str account-id "-" location)
|
||||
:location (or location "")
|
||||
:count count
|
||||
:debits debit
|
||||
:credits credit
|
||||
:amount (if account-type (if (#{:account-type/asset
|
||||
:account-type/dividend
|
||||
:account-type/expense} account-type)
|
||||
(- debit credit)
|
||||
(- credit debit))
|
||||
0.0)}
|
||||
account))))
|
||||
[]))))
|
||||
|
||||
(defn full-ledger-for-client [client-id]
|
||||
(->> (dc/q
|
||||
{:find ['?d '?jel '?account '?location '?debit '?credit]
|
||||
:in ['$ '?client-id]
|
||||
:where '[[?e :journal-entry/client ?client-id]
|
||||
[?e :journal-entry/date ?d]
|
||||
[?e :journal-entry/line-items ?jel]
|
||||
(or-join [?e]
|
||||
(and [?e :journal-entry/original-entity ?i]
|
||||
(or-join [?e ?i]
|
||||
(and
|
||||
[?i :transaction/bank-account ?b]
|
||||
(or [?b :bank-account/include-in-reports true]
|
||||
(not [?b :bank-account/include-in-reports])))
|
||||
(not [?i :transaction/bank-account])))
|
||||
(not [?e :journal-entry/original-entity ]))
|
||||
[(get-else $ ?jel :journal-entry-line/account :account/unknown) ?account]
|
||||
[(get-else $ ?jel :journal-entry-line/debit 0.0) ?debit ]
|
||||
[(get-else $ ?jel :journal-entry-line/credit 0.0) ?credit]
|
||||
[(get-else $ ?jel :journal-entry-line/location "") ?location]]}
|
||||
(dc/db conn) client-id)
|
||||
(->> (dc/q
|
||||
{:find ['?d '?jel '?account '?location '?debit '?credit]
|
||||
:in ['$ '?client-id]
|
||||
:where '[[?e :journal-entry/client ?client-id]
|
||||
[?e :journal-entry/date ?d]
|
||||
[?e :journal-entry/line-items ?jel]
|
||||
(or-join [?e]
|
||||
(and [?e :journal-entry/original-entity ?i]
|
||||
(or-join [?e ?i]
|
||||
(and
|
||||
[?i :transaction/bank-account ?b]
|
||||
(or [?b :bank-account/include-in-reports true]
|
||||
(not [?b :bank-account/include-in-reports])))
|
||||
(not [?i :transaction/bank-account])))
|
||||
(not [?e :journal-entry/original-entity]))
|
||||
[(get-else $ ?jel :journal-entry-line/account :account/unknown) ?account]
|
||||
[(get-else $ ?jel :journal-entry-line/debit 0.0) ?debit]
|
||||
[(get-else $ ?jel :journal-entry-line/credit 0.0) ?credit]
|
||||
[(get-else $ ?jel :journal-entry-line/location "") ?location]]}
|
||||
(dc/db conn) client-id)
|
||||
(sort-by first)))
|
||||
|
||||
(defn get-balance-sheet [context args _]
|
||||
@@ -180,30 +174,29 @@
|
||||
[client-id (build-account-lookup client-id)]))
|
||||
(into {}))]
|
||||
(alog/info ::balance-sheet :params args)
|
||||
|
||||
|
||||
(cond-> {:balance-sheet-accounts (mapcat
|
||||
#(roll-up-until (lookup-account %) (all-ledger-entries %) end-date )
|
||||
client-ids)
|
||||
}
|
||||
#(roll-up-until (lookup-account %) (all-ledger-entries %) end-date)
|
||||
client-ids)}
|
||||
(:include_comparison args) (assoc :comparable-balance-sheet-accounts (mapcat
|
||||
#(roll-up-until (lookup-account %) (all-ledger-entries %) comparable-date )
|
||||
client-ids))
|
||||
#(roll-up-until (lookup-account %) (all-ledger-entries %) comparable-date)
|
||||
client-ids))
|
||||
true ->graphql)))
|
||||
|
||||
(defn get-profit-and-loss-raw [client-ids periods]
|
||||
(let [ all-ledger-entries (->> client-ids
|
||||
(map (fn [client-id]
|
||||
[client-id (full-ledger-for-client client-id)]))
|
||||
(into {}))
|
||||
(let [all-ledger-entries (->> client-ids
|
||||
(map (fn [client-id]
|
||||
[client-id (full-ledger-for-client client-id)]))
|
||||
(into {}))
|
||||
lookup-account (->> client-ids
|
||||
(map (fn [client-id]
|
||||
[client-id (build-account-lookup client-id)]))
|
||||
(into {}))]
|
||||
(->graphql {:periods
|
||||
(->graphql {:periods
|
||||
(->> periods
|
||||
(mapv (fn [{:keys [start end]}]
|
||||
{:accounts (mapcat
|
||||
#(roll-up-until (lookup-account %) (all-ledger-entries %) (coerce/to-date end) (coerce/to-date start) )
|
||||
#(roll-up-until (lookup-account %) (all-ledger-entries %) (coerce/to-date end) (coerce/to-date start))
|
||||
client-ids)})))})))
|
||||
|
||||
(defn get-profit-and-loss [context args _]
|
||||
@@ -216,12 +209,9 @@
|
||||
(assert-can-see-client (:id context) client-id))
|
||||
_ (when (and (:include_deltas args)
|
||||
(:column_per_location args))
|
||||
(throw (ex-info "Please select one of 'Include deltas' or 'Column per location'" {:validation-error "Please select one of 'Include deltas' or 'Column per location'"}))) ]
|
||||
(throw (ex-info "Please select one of 'Include deltas' or 'Column per location'" {:validation-error "Please select one of 'Include deltas' or 'Column per location'"})))]
|
||||
(get-profit-and-loss-raw client-ids (:periods args))))
|
||||
|
||||
|
||||
|
||||
|
||||
;; profit and loss based off of index
|
||||
#_(defn get-profit-and-loss [context args _]
|
||||
(let [client-id (:client_id args)
|
||||
@@ -239,17 +229,17 @@
|
||||
:in $ [?c ...]
|
||||
:where
|
||||
(or-join [?c ?a ?l]
|
||||
(and
|
||||
[?a :account/numeric-code]
|
||||
(not [?a :account/location])
|
||||
[?c :client/locations ?l])
|
||||
(and
|
||||
[?a :account/numeric-code]
|
||||
[?a :account/location ?l]
|
||||
[?c :client/locations ?l])
|
||||
[?a :account/numeric-code]
|
||||
(not [?a :account/location])
|
||||
[?c :client/locations ?l])
|
||||
(and
|
||||
[?c :client/bank-accounts ?a]
|
||||
[(ground "A") ?l]))]
|
||||
[?a :account/numeric-code]
|
||||
[?a :account/location ?l]
|
||||
[?c :client/locations ?l])
|
||||
(and
|
||||
[?c :client/bank-accounts ?a]
|
||||
[(ground "A") ?l]))]
|
||||
(dc/db conn)
|
||||
client-ids)
|
||||
lookup-account (->> client-ids
|
||||
@@ -257,49 +247,48 @@
|
||||
[client-id (build-account-lookup client-id)]))
|
||||
(into {}))]
|
||||
(->graphql
|
||||
{:periods
|
||||
(->> (:periods args)
|
||||
(mapv (fn [{:keys [start end]}]
|
||||
(let [start (coerce/to-date start)
|
||||
end (coerce/to-date end)]
|
||||
{:accounts (mapcat
|
||||
(fn [[c a l]]
|
||||
(let [start-point (->> (dc/index-pull db
|
||||
{:index :avet
|
||||
:selector [:db/id :journal-entry-line/running-balance :journal-entry-line/client+account+location+date]
|
||||
:start [:journal-entry-line/client+account+location+date [c a l start]]
|
||||
:reverse true
|
||||
:limit 1})
|
||||
(take-while (fn [result]
|
||||
(= [c a l]
|
||||
(take 3 (:journal-entry-line/client+account+location+date result)))))
|
||||
(drop-while (fn [{[_ _ _ date] :journal-entry-line/client+account+location+date}]
|
||||
(>= (compare date start) 0)))
|
||||
first)
|
||||
end-point (->> (dc/index-pull db
|
||||
{:index :avet
|
||||
:selector [:db/id :journal-entry-line/running-balance :journal-entry-line/client+account+location+date]
|
||||
:start [:journal-entry-line/client+account+location+date [c a l end]]
|
||||
:reverse true
|
||||
:limit 1})
|
||||
(take-while (fn [result]
|
||||
(= [c a l]
|
||||
(take 3 (:journal-entry-line/client+account+location+date result)))))
|
||||
(take 1)
|
||||
(drop-while (fn [{[_ _ _ date] :journal-entry-line/client+account+location+date}]
|
||||
(>= (compare date end) 0)))
|
||||
first)]
|
||||
(when end-point
|
||||
[(merge {:id (str a "-" l)
|
||||
:location (or l "")
|
||||
:count 0
|
||||
:debits 0
|
||||
:credits 0
|
||||
:amount (- (or (:journal-entry-line/running-balance end-point) 0.0)
|
||||
(or (:journal-entry-line/running-balance start-point) 0.0))
|
||||
}
|
||||
((lookup-account c) a))])))
|
||||
all-used-account-locations)}))))})))
|
||||
{:periods
|
||||
(->> (:periods args)
|
||||
(mapv (fn [{:keys [start end]}]
|
||||
(let [start (coerce/to-date start)
|
||||
end (coerce/to-date end)]
|
||||
{:accounts (mapcat
|
||||
(fn [[c a l]]
|
||||
(let [start-point (->> (dc/index-pull db
|
||||
{:index :avet
|
||||
:selector [:db/id :journal-entry-line/running-balance :journal-entry-line/client+account+location+date]
|
||||
:start [:journal-entry-line/client+account+location+date [c a l start]]
|
||||
:reverse true
|
||||
:limit 1})
|
||||
(take-while (fn [result]
|
||||
(= [c a l]
|
||||
(take 3 (:journal-entry-line/client+account+location+date result)))))
|
||||
(drop-while (fn [{[_ _ _ date] :journal-entry-line/client+account+location+date}]
|
||||
(>= (compare date start) 0)))
|
||||
first)
|
||||
end-point (->> (dc/index-pull db
|
||||
{:index :avet
|
||||
:selector [:db/id :journal-entry-line/running-balance :journal-entry-line/client+account+location+date]
|
||||
:start [:journal-entry-line/client+account+location+date [c a l end]]
|
||||
:reverse true
|
||||
:limit 1})
|
||||
(take-while (fn [result]
|
||||
(= [c a l]
|
||||
(take 3 (:journal-entry-line/client+account+location+date result)))))
|
||||
(take 1)
|
||||
(drop-while (fn [{[_ _ _ date] :journal-entry-line/client+account+location+date}]
|
||||
(>= (compare date end) 0)))
|
||||
first)]
|
||||
(when end-point
|
||||
[(merge {:id (str a "-" l)
|
||||
:location (or l "")
|
||||
:count 0
|
||||
:debits 0
|
||||
:credits 0
|
||||
:amount (- (or (:journal-entry-line/running-balance end-point) 0.0)
|
||||
(or (:journal-entry-line/running-balance start-point) 0.0))}
|
||||
((lookup-account c) a))])))
|
||||
all-used-account-locations)}))))})))
|
||||
|
||||
(defn profit-and-loss-pdf [context args value]
|
||||
(let [data (get-profit-and-loss context args value)
|
||||
@@ -320,10 +309,9 @@
|
||||
|
||||
(->graphql result)))
|
||||
|
||||
|
||||
(defn assoc-error [f]
|
||||
(fn [entry]
|
||||
(try
|
||||
(try
|
||||
(f entry)
|
||||
(catch Exception e
|
||||
(assoc entry :error (.getMessage e)
|
||||
@@ -333,13 +321,13 @@
|
||||
(defn all-ids-not-locked [all-ids]
|
||||
(->> all-ids
|
||||
(dc/q '[:find ?t
|
||||
:in $ [?t ...]
|
||||
:where
|
||||
[?t :journal-entry/client ?c]
|
||||
[(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
|
||||
[?t :journal-entry/date ?d]
|
||||
[(>= ?d ?lu)]]
|
||||
(dc/db conn))
|
||||
:in $ [?t ...]
|
||||
:where
|
||||
[?t :journal-entry/client ?c]
|
||||
[(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
|
||||
[?t :journal-entry/date ?d]
|
||||
[(>= ?d ?lu)]]
|
||||
(dc/db conn))
|
||||
(map first)))
|
||||
|
||||
(defn delete-external-ledger [context args _]
|
||||
@@ -353,8 +341,8 @@
|
||||
(#(l/raw-graphql-ids (dc/db conn) %))
|
||||
:ids)
|
||||
_ (alog/info ::trying-to-delete
|
||||
:count (count ids)
|
||||
:sample (take 3 ids))
|
||||
:count (count ids)
|
||||
:sample (take 3 ids))
|
||||
specific-ids (l/filter-ids (:ids args))
|
||||
all-ids (all-ids-not-locked (into (set ids) specific-ids))]
|
||||
(if (> (count all-ids) 1000)
|
||||
@@ -364,7 +352,7 @@
|
||||
(audit-transact-batch
|
||||
(map (fn [i]
|
||||
[:db/retractEntity i])
|
||||
all-ids)
|
||||
all-ids)
|
||||
(:id context))
|
||||
{:message (str "Succesfully deleted " (count all-ids) " ledger entries.")}))))
|
||||
|
||||
@@ -372,15 +360,15 @@
|
||||
(assert-admin (:id context))
|
||||
(let [used-vendor-names (set (map :vendor_name (:entries args)))
|
||||
all-vendors (mu/trace ::get-all-vendors
|
||||
[]
|
||||
(->> (dc/q '[:find ?e
|
||||
:in $ [?name ...]
|
||||
:where [?e :vendor/name ?name]]
|
||||
(dc/db conn)
|
||||
used-vendor-names)
|
||||
(map first)
|
||||
(pull-many (dc/db conn) [:db/id :vendor/name])
|
||||
(by :vendor/name)))
|
||||
[]
|
||||
(->> (dc/q '[:find ?e
|
||||
:in $ [?name ...]
|
||||
:where [?e :vendor/name ?name]]
|
||||
(dc/db conn)
|
||||
used-vendor-names)
|
||||
(map first)
|
||||
(pull-many (dc/db conn) [:db/id :vendor/name])
|
||||
(by :vendor/name)))
|
||||
client-locked-lookup (mu/trace ::get-all-clients []
|
||||
(->> (dc/q '[:find ?code ?locked-until
|
||||
:in $
|
||||
@@ -389,18 +377,18 @@
|
||||
(dc/db conn))
|
||||
(into {})))
|
||||
all-client-bank-accounts (mu/trace ::get-all-client-bank-accounts
|
||||
[]
|
||||
(->> (dc/q '[:find ?code ?ba-code
|
||||
:in $
|
||||
:where [?c :client/code ?code]
|
||||
[?c :client/bank-accounts ?ba]
|
||||
[?ba :bank-account/code ?ba-code]]
|
||||
(dc/db conn))
|
||||
(reduce
|
||||
(fn [acc [code ba-code]]
|
||||
(update acc code (fnil conj #{}) ba-code))
|
||||
{})))
|
||||
|
||||
[]
|
||||
(->> (dc/q '[:find ?code ?ba-code
|
||||
:in $
|
||||
:where [?c :client/code ?code]
|
||||
[?c :client/bank-accounts ?ba]
|
||||
[?ba :bank-account/code ?ba-code]]
|
||||
(dc/db conn))
|
||||
(reduce
|
||||
(fn [acc [code ba-code]]
|
||||
(update acc code (fnil conj #{}) ba-code))
|
||||
{})))
|
||||
|
||||
all-client-locations (mu/trace ::get-all-client-locations
|
||||
[]
|
||||
(->> (dc/q '[:find ?code ?location
|
||||
@@ -409,160 +397,158 @@
|
||||
[?c :client/locations ?location]]
|
||||
(dc/db conn))
|
||||
(reduce
|
||||
(fn [acc [code ba-code]]
|
||||
(update acc code (fnil conj #{"HQ" "A"}) ba-code))
|
||||
{})))
|
||||
|
||||
(fn [acc [code ba-code]]
|
||||
(update acc code (fnil conj #{"HQ" "A"}) ba-code))
|
||||
{})))
|
||||
|
||||
new-hidden-vendors (reduce
|
||||
(fn [new-vendors {:keys [vendor_name]}]
|
||||
(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})))
|
||||
{}
|
||||
(:entries args))
|
||||
(fn [new-vendors {:keys [vendor_name]}]
|
||||
(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})))
|
||||
{}
|
||||
(:entries args))
|
||||
_ (mu/trace ::upsert-new-vendors
|
||||
[]
|
||||
(audit-transact-batch (vec (vals new-hidden-vendors)) (:id context)))
|
||||
[]
|
||||
(audit-transact-batch (vec (vals new-hidden-vendors)) (:id context)))
|
||||
all-vendors (->> (dc/q '[:find ?e
|
||||
:in $ [?name ...]
|
||||
:where [?e :vendor/name ?name]]
|
||||
(dc/db conn)
|
||||
used-vendor-names)
|
||||
:in $ [?name ...]
|
||||
:where [?e :vendor/name ?name]]
|
||||
(dc/db conn)
|
||||
used-vendor-names)
|
||||
(map first)
|
||||
(pull-many (dc/db conn) [:db/id :vendor/name])
|
||||
(by :vendor/name))
|
||||
all-accounts (mu/trace ::get-all-accounts []
|
||||
(transduce (map (comp str :account/numeric-code)) conj #{} (a/get-accounts)))
|
||||
transaction (mu/trace ::build-transaction
|
||||
[:count (count (:entries args))]
|
||||
(doall (map
|
||||
(assoc-error (fn [entry]
|
||||
(let [vendor (all-vendors (:vendor_name entry))]
|
||||
(when-not (client-locked-lookup (:client_code entry))
|
||||
(throw (ex-info (str "Client '" (:client_code entry )"' not found.") {:status :error}) ))
|
||||
(when-not vendor
|
||||
(throw (ex-info (str "Vendor '" (:vendor_name entry) "' not found.") {:status :error})))
|
||||
(when-not (re-find #"\d{1,2}/\d{1,2}/\d{4}" (:date entry))
|
||||
(throw (ex-info (str "Date must be MM/dd/yyyy") {:status :error})))
|
||||
(when-let [locked-until (client-locked-lookup (:client_code entry))]
|
||||
(when (and (not (t/after? (coerce/to-date-time (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry))))
|
||||
(coerce/to-date-time locked-until)))
|
||||
(not (t/equal? (coerce/to-date-time (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry))))
|
||||
(coerce/to-date-time locked-until))))
|
||||
(throw (ex-info (str "Client's data is locked until " locked-until) {:status :error}))))
|
||||
|
||||
(when-not (dollars= (reduce (fnil + 0.0 0.0) 0.0 (map :debit (:line_items entry)))
|
||||
(reduce (fnil + 0.0 0.0) 0.0 (map :credit (:line_items entry))))
|
||||
(throw (ex-info (str "Debits '"
|
||||
(reduce (fnil + 0.0 0.0) 0 (map :debit (:line_items entry)))
|
||||
"' and credits '"
|
||||
(reduce (fnil + 0.0 0.0) 0 (map :credit (:line_items entry)))
|
||||
"' do not add up.")
|
||||
{:status :error})))
|
||||
(when (dollars= (reduce (fnil + 0.0 0.0) 0.0 (map :debit (:line_items entry)))
|
||||
0.0)
|
||||
(throw (ex-info (str "Cannot have ledger entries that total $0.00")
|
||||
{:status :ignored})))
|
||||
(assoc entry
|
||||
:status :success
|
||||
:tx
|
||||
[:upsert-ledger
|
||||
(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 (:db/id (all-vendors (:vendor_name entry)))
|
||||
:journal-entry/amount (:amount entry)
|
||||
:journal-entry/note (:note entry)
|
||||
:journal-entry/cleared-against (:cleared_against entry)
|
||||
[:count (count (:entries args))]
|
||||
(doall (map
|
||||
(assoc-error (fn [entry]
|
||||
(let [vendor (all-vendors (:vendor_name entry))]
|
||||
(when-not (client-locked-lookup (:client_code entry))
|
||||
(throw (ex-info (str "Client '" (:client_code entry) "' not found.") {:status :error})))
|
||||
(when-not vendor
|
||||
(throw (ex-info (str "Vendor '" (:vendor_name entry) "' not found.") {:status :error})))
|
||||
(when-not (re-find #"\d{1,2}/\d{1,2}/\d{4}" (:date entry))
|
||||
(throw (ex-info (str "Date must be MM/dd/yyyy") {:status :error})))
|
||||
(when-let [locked-until (client-locked-lookup (:client_code entry))]
|
||||
(when (and (not (t/after? (coerce/to-date-time (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry))))
|
||||
(coerce/to-date-time locked-until)))
|
||||
(not (t/equal? (coerce/to-date-time (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry))))
|
||||
(coerce/to-date-time locked-until))))
|
||||
(throw (ex-info (str "Client's data is locked until " locked-until) {:status :error}))))
|
||||
|
||||
:journal-entry/line-items
|
||||
(mapv (fn [ea]
|
||||
(let [debit (or (:debit ea) 0.0)
|
||||
credit (or (:credit ea) 0.0)]
|
||||
(when (and (not (get
|
||||
(get all-client-locations (:client_code entry))
|
||||
(:location ea)))
|
||||
(not= "A" (:location ea)))
|
||||
(throw (ex-info (str "Location '" (:location ea) "' not found.")
|
||||
{:status :error})))
|
||||
(when (and (<= debit 0.0)
|
||||
(<= credit 0.0))
|
||||
(throw (ex-info (str "Line item amount " (or debit credit) " must be greater than 0.")
|
||||
{:status :error})))
|
||||
(when (and (not (all-accounts (:account_identifier ea)))
|
||||
(not (get
|
||||
(get all-client-bank-accounts (:client_code entry))
|
||||
(:account_identifier ea))))
|
||||
(throw (ex-info (str "Account '" (:account_identifier ea) "' not found.")
|
||||
{:status :error})))
|
||||
(let [matching-account (when (re-matches #"^[0-9]+$" (:account_identifier ea))
|
||||
(a/get-account-by-numeric-code-and-sets (Integer/parseInt (:account_identifier ea)) ["default"]))]
|
||||
(when (and matching-account
|
||||
(:account/location matching-account)
|
||||
(not= (:account/location matching-account)
|
||||
(:location ea)))
|
||||
(throw (ex-info (str "Account '"
|
||||
(:account/numeric-code matching-account)
|
||||
"' requires location '"
|
||||
(:account/location matching-account)
|
||||
"' but got '"
|
||||
(:location ea)
|
||||
"'")
|
||||
{:status :error})))
|
||||
(when (and matching-account
|
||||
(not (:account/location matching-account))
|
||||
(= "A" (:location ea)))
|
||||
(throw (ex-info (str "Account '"
|
||||
(:account/numeric-code matching-account)
|
||||
"' cannot use location '"
|
||||
(:location ea)
|
||||
"'")
|
||||
{:status :error})))
|
||||
(remove-nils (cond-> {:db/id (random-tempid)
|
||||
:journal-entry-line/location (:location ea)
|
||||
:journal-entry-line/debit (when (> debit 0)
|
||||
debit)
|
||||
:journal-entry-line/credit (when (> credit 0)
|
||||
credit)}
|
||||
matching-account (assoc :journal-entry-line/account (:db/id matching-account))
|
||||
(not matching-account) (assoc :journal-entry-line/account [:bank-account/code (:account_identifier ea)]))))))
|
||||
(:line_items entry))
|
||||
|
||||
:journal-entry/cleared true})]))))
|
||||
(:entries args))))
|
||||
(when-not (dollars= (reduce (fnil + 0.0 0.0) 0.0 (map :debit (:line_items entry)))
|
||||
(reduce (fnil + 0.0 0.0) 0.0 (map :credit (:line_items entry))))
|
||||
(throw (ex-info (str "Debits '"
|
||||
(reduce (fnil + 0.0 0.0) 0 (map :debit (:line_items entry)))
|
||||
"' and credits '"
|
||||
(reduce (fnil + 0.0 0.0) 0 (map :credit (:line_items entry)))
|
||||
"' do not add up.")
|
||||
{:status :error})))
|
||||
(when (dollars= (reduce (fnil + 0.0 0.0) 0.0 (map :debit (:line_items entry)))
|
||||
0.0)
|
||||
(throw (ex-info (str "Cannot have ledger entries that total $0.00")
|
||||
{:status :ignored})))
|
||||
(assoc entry
|
||||
:status :success
|
||||
:tx
|
||||
[:upsert-ledger
|
||||
(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 (:db/id (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
|
||||
(mapv (fn [ea]
|
||||
(let [debit (or (:debit ea) 0.0)
|
||||
credit (or (:credit ea) 0.0)]
|
||||
(when (and (not (get
|
||||
(get all-client-locations (:client_code entry))
|
||||
(:location ea)))
|
||||
(not= "A" (:location ea)))
|
||||
(throw (ex-info (str "Location '" (:location ea) "' not found.")
|
||||
{:status :error})))
|
||||
(when (and (<= debit 0.0)
|
||||
(<= credit 0.0))
|
||||
(throw (ex-info (str "Line item amount " (or debit credit) " must be greater than 0.")
|
||||
{:status :error})))
|
||||
(when (and (not (all-accounts (:account_identifier ea)))
|
||||
(not (get
|
||||
(get all-client-bank-accounts (:client_code entry))
|
||||
(:account_identifier ea))))
|
||||
(throw (ex-info (str "Account '" (:account_identifier ea) "' not found.")
|
||||
{:status :error})))
|
||||
(let [matching-account (when (re-matches #"^[0-9]+$" (:account_identifier ea))
|
||||
(a/get-account-by-numeric-code-and-sets (Integer/parseInt (:account_identifier ea)) ["default"]))]
|
||||
(when (and matching-account
|
||||
(:account/location matching-account)
|
||||
(not= (:account/location matching-account)
|
||||
(:location ea)))
|
||||
(throw (ex-info (str "Account '"
|
||||
(:account/numeric-code matching-account)
|
||||
"' requires location '"
|
||||
(:account/location matching-account)
|
||||
"' but got '"
|
||||
(:location ea)
|
||||
"'")
|
||||
{:status :error})))
|
||||
(when (and matching-account
|
||||
(not (:account/location matching-account))
|
||||
(= "A" (:location ea)))
|
||||
(throw (ex-info (str "Account '"
|
||||
(:account/numeric-code matching-account)
|
||||
"' cannot use location '"
|
||||
(:location ea)
|
||||
"'")
|
||||
{:status :error})))
|
||||
(remove-nils (cond-> {:db/id (random-tempid)
|
||||
:journal-entry-line/location (:location ea)
|
||||
:journal-entry-line/debit (when (> debit 0)
|
||||
debit)
|
||||
:journal-entry-line/credit (when (> credit 0)
|
||||
credit)}
|
||||
matching-account (assoc :journal-entry-line/account (:db/id matching-account))
|
||||
(not matching-account) (assoc :journal-entry-line/account [:bank-account/code (:account_identifier ea)]))))))
|
||||
(:line_items entry))
|
||||
|
||||
:journal-entry/cleared true})]))))
|
||||
(:entries args))))
|
||||
errors (filter #(= (:status %) :error) transaction)
|
||||
ignored (filter #(= (:status %) :ignored) transaction)
|
||||
success (filter #(= (:status %) :success) transaction)
|
||||
retraction (mapv (fn [x] [:db/retractEntity [:journal-entry/external-id (:external_id x)]])
|
||||
success)
|
||||
ignore-retraction (->> ignored
|
||||
(map :external_id )
|
||||
(map :external_id)
|
||||
(dc/q '[:find ?je
|
||||
:in $ [?ei ...]
|
||||
:where [?je :journal-entry/external-id ?ei]]
|
||||
(dc/db conn)
|
||||
)
|
||||
:in $ [?ei ...]
|
||||
:where [?je :journal-entry/external-id ?ei]]
|
||||
(dc/db conn))
|
||||
(map first)
|
||||
(map (fn [je] [:db/retractEntity je])))]
|
||||
(alog/info ::manual-import
|
||||
:errors (count errors)
|
||||
:sample (take 3 errors))
|
||||
|
||||
|
||||
(mu/trace ::retraction-tx
|
||||
[:count (count retraction)]
|
||||
(audit-transact-batch retraction (:id context)))
|
||||
[:count (count retraction)]
|
||||
(audit-transact-batch retraction (:id context)))
|
||||
(mu/trace ::ignore-retraction-tx
|
||||
[:count (count ignore-retraction)]
|
||||
(when (seq ignore-retraction)
|
||||
(audit-transact-batch ignore-retraction (:id context))))
|
||||
(let [invalidated
|
||||
[:count (count ignore-retraction)]
|
||||
(when (seq ignore-retraction)
|
||||
(audit-transact-batch ignore-retraction (:id context))))
|
||||
(let [invalidated
|
||||
(mu/trace ::success-tx
|
||||
[:count (count success)]
|
||||
(for [[_ n] (:tempids (audit-transact-batch (map :tx success) (:id context)))]
|
||||
@@ -573,7 +559,7 @@
|
||||
[:count (count invalidated)]
|
||||
(doseq [n invalidated]
|
||||
(solr/touch n)))))
|
||||
|
||||
|
||||
{:successful (map (fn [x] {:external_id (:external_id x)}) success)
|
||||
:ignored (map (fn [x]
|
||||
{:external_id (:external_id x)})
|
||||
@@ -582,7 +568,6 @@
|
||||
:errors (map (fn [x] {:external_id (:external_id x)
|
||||
:error (:error x)}) errors)}))
|
||||
|
||||
|
||||
(defn get-journal-detail-report [context input _]
|
||||
(let [category-totals (atom {})
|
||||
base-categories (into []
|
||||
@@ -597,20 +582,19 @@
|
||||
:clients [{:db/id client-id}])
|
||||
{:filters {:location location
|
||||
:date_range (:date_range input)
|
||||
:from_numeric_code (l-reports/min-numeric-code category )
|
||||
:to_numeric_code (l-reports/max-numeric-code category )
|
||||
:from_numeric_code (l-reports/min-numeric-code category)
|
||||
:to_numeric_code (l-reports/max-numeric-code category)
|
||||
:per_page Integer/MAX_VALUE}}
|
||||
nil)
|
||||
:journal_entries
|
||||
(mapcat (fn [je]
|
||||
(->> (je :line_items)
|
||||
(filter (fn [jel]
|
||||
(when-let [account (account-lookup (:id (:account jel)))]
|
||||
(and
|
||||
(l-reports/account-belongs-in-category? (:numeric_code account) category)
|
||||
(= location (:location jel)))))
|
||||
)
|
||||
(map (fn [jel ]
|
||||
(when-let [account (account-lookup (:id (:account jel)))]
|
||||
(and
|
||||
(l-reports/account-belongs-in-category? (:numeric_code account) category)
|
||||
(= location (:location jel))))))
|
||||
(map (fn [jel]
|
||||
{:date (:date je)
|
||||
:debit (:debit jel)
|
||||
:credit (:credit jel)
|
||||
@@ -621,18 +605,18 @@
|
||||
(into []))
|
||||
_ (swap! category-totals assoc-in [client-id location category]
|
||||
(- (or (reduce + 0.0 (map #(or (:credit %) 0.0) all-journal-entries)) 0.0)
|
||||
(or (reduce + 0.0 (map #(or (:debit %) 0.0) all-journal-entries)) 0.0)) )
|
||||
(or (reduce + 0.0 (map #(or (:debit %) 0.0) all-journal-entries)) 0.0)))
|
||||
journal-entries-by-account (group-by #(account-lookup (get-in % [:account :id])) all-journal-entries)]
|
||||
[account journal-entries] (conj (vec journal-entries-by-account) [nil all-journal-entries])
|
||||
:let [journal-entries (first (reduce
|
||||
(fn [[acc last-je] je]
|
||||
(let [next-je (assoc je :running_balance
|
||||
(- (+ (or (:running_balance last-je 0.0) 0.0)
|
||||
(or (:credit je 0.0) 0.0))
|
||||
(or (:debit je 0.0) 0.0)))]
|
||||
[(conj acc next-je) next-je]))
|
||||
[]
|
||||
(sort-by :date journal-entries)))]]
|
||||
(fn [[acc last-je] je]
|
||||
(let [next-je (assoc je :running_balance
|
||||
(- (+ (or (:running_balance last-je 0.0) 0.0)
|
||||
(or (:credit je 0.0) 0.0))
|
||||
(or (:debit je 0.0) 0.0)))]
|
||||
[(conj acc next-je) next-je]))
|
||||
[]
|
||||
(sort-by :date journal-entries)))]]
|
||||
{:category (->graphql category)
|
||||
:client_id client-id
|
||||
:location location
|
||||
@@ -641,7 +625,7 @@
|
||||
:journal_entries (when account journal-entries)
|
||||
:total (- (or (reduce + 0.0 (map #(or (:credit %) 0.0) journal-entries)) 0.0)
|
||||
(or (reduce + 0.0 (map #(or (:debit %) 0.0) journal-entries)) 0.0))}))
|
||||
result {:categories
|
||||
result {:categories
|
||||
(into base-categories
|
||||
(for [client-id (:client_ids input)
|
||||
:let [_ (assert-can-see-client (:id context) client-id)
|
||||
@@ -675,15 +659,12 @@
|
||||
line))}]
|
||||
result))
|
||||
|
||||
|
||||
|
||||
(defn journal-detail-report-pdf [context args value]
|
||||
(let [data (get-journal-detail-report context args value)
|
||||
result (print-journal-detail-report (:id context) args data)]
|
||||
|
||||
(->graphql result)))
|
||||
|
||||
|
||||
(def objects
|
||||
{:balance_sheet_account
|
||||
{:fields {:id {:type 'String}
|
||||
@@ -847,7 +828,7 @@
|
||||
(def input-objects
|
||||
{:numeric_code_range
|
||||
{:fields {:from {:type 'Int}
|
||||
:to {:type 'Int}}}
|
||||
:to {:type 'Int}}}
|
||||
:ledger_filters
|
||||
{:fields {:client_id {:type :id}
|
||||
:vendor_id {:type :id}
|
||||
@@ -874,25 +855,23 @@
|
||||
:credit {:type :money}}}
|
||||
:import_ledger_entry
|
||||
{:fields {:source {:type 'String}
|
||||
:external_id {:type 'String}
|
||||
:external_id {:type 'String}
|
||||
:client_code {:type 'String}
|
||||
:date {:type 'String}
|
||||
:vendor_name {:type 'String}
|
||||
:amount {:type :money}
|
||||
:note {:type 'String}
|
||||
:cleared_against {:type 'String}
|
||||
:line_items {:type '(list :import_ledger_line_item)}}}
|
||||
})
|
||||
:line_items {:type '(list :import_ledger_line_item)}}}})
|
||||
|
||||
(def enums
|
||||
{:ledger_category {:values [{:enum-value :sales}
|
||||
{:enum-value :cogs}
|
||||
{:enum-value :payroll}
|
||||
{:enum-value :cogs}
|
||||
{:enum-value :payroll}
|
||||
{:enum-value :controllable}
|
||||
{:enum-value :fixed_overhead}
|
||||
{:enum-value :ownership_controllable}]}})
|
||||
|
||||
|
||||
(def resolvers
|
||||
{:get-ledger-page get-ledger-page
|
||||
:get-balance-sheet get-balance-sheet
|
||||
|
||||
@@ -21,13 +21,11 @@
|
||||
:name (first name)}))
|
||||
[]))
|
||||
|
||||
|
||||
|
||||
(defn attach [schema]
|
||||
(->
|
||||
(->
|
||||
(merge-with merge schema
|
||||
{:objects {:plaid_link_result
|
||||
{:fields {:token {:type 'String}} }
|
||||
{:fields {:token {:type 'String}}}
|
||||
|
||||
:plaid_item
|
||||
{:fields {:external_id {:type 'String}
|
||||
@@ -50,7 +48,7 @@
|
||||
:name {:type 'String}
|
||||
:number {:type 'String}}}}
|
||||
:queries {:search_plaid_merchants {:type '(list :plaid_merchant)
|
||||
:args {:query {:type 'String}}
|
||||
:resolve :search-plaid-merchants}}})
|
||||
:args {:query {:type 'String}}
|
||||
:resolve :search-plaid-merchants}}})
|
||||
(attach-tracing-resolvers {:search-plaid-merchants search-merchants})))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns auto-ap.graphql.sales-orders
|
||||
(:require [auto-ap.datomic.sales-orders :as d-sales-orders2]
|
||||
[auto-ap.graphql.utils :refer [->graphql <-graphql result->page assert-admin] ]
|
||||
[auto-ap.graphql.utils :refer [->graphql <-graphql result->page assert-admin]]
|
||||
[com.walmartlabs.lacinia.util :refer [attach-resolvers]]
|
||||
[auto-ap.graphql.utils :refer [attach-tracing-resolvers]]))
|
||||
|
||||
@@ -14,19 +14,18 @@
|
||||
(defn get-all-sales-orders [context args _]
|
||||
(assert-admin (:id context))
|
||||
(map
|
||||
->graphql
|
||||
(first (d-sales-orders2/get-graphql (assoc (<-graphql args) :count Integer/MAX_VALUE)))))
|
||||
|
||||
->graphql
|
||||
(first (d-sales-orders2/get-graphql (assoc (<-graphql args) :count Integer/MAX_VALUE)))))
|
||||
|
||||
(def objects
|
||||
{:sales_order_page
|
||||
{:fields {:sales_orders {:type '(list :sales_order)}
|
||||
:count {:type 'Int}
|
||||
:total {:type 'Int}
|
||||
:start {:type 'Int}
|
||||
:end {:type 'Int}
|
||||
:sales_order_total {:type :money}
|
||||
:sales_order_tax {:type :money}}}
|
||||
:count {:type 'Int}
|
||||
:total {:type 'Int}
|
||||
:start {:type 'Int}
|
||||
:end {:type 'Int}
|
||||
:sales_order_total {:type :money}
|
||||
:sales_order_tax {:type :money}}}
|
||||
|
||||
:sales_order
|
||||
{:fields {:id {:type :id}
|
||||
@@ -93,8 +92,7 @@
|
||||
|
||||
(def resolvers
|
||||
{:get-all-sales-orders get-all-sales-orders
|
||||
:get-sales-order-page get-sales-orders-page
|
||||
})
|
||||
:get-sales-order-page get-sales-orders-page})
|
||||
|
||||
(defn attach [schema]
|
||||
(->
|
||||
|
||||
@@ -29,9 +29,8 @@
|
||||
(defn get-transaction-rule-matches [context args _]
|
||||
(if (= "admin" (:user/role (:id context)))
|
||||
(let [transaction (update (d-transactions/get-by-id (:transaction_id args)) :transaction/date c/to-date)
|
||||
all-rules (tr/get-all-for-client (:db/id (:transaction/client transaction)))
|
||||
all-rules (tr/get-all-for-client (:db/id (:transaction/client transaction)))]
|
||||
|
||||
]
|
||||
(mu/log ::counted
|
||||
:count (count all-rules))
|
||||
(doto (map ->graphql (rm/get-matching-rules transaction all-rules)) (#(println (count %)))))
|
||||
@@ -43,7 +42,7 @@
|
||||
:account account_id
|
||||
:location location})
|
||||
|
||||
(defn delete-transaction-rule [context {:keys [transaction_rule_id ]} _]
|
||||
(defn delete-transaction-rule [context {:keys [transaction_rule_id]} _]
|
||||
(assert-admin (:id context))
|
||||
(let [existing-transaction-rule (tr/get-by-id transaction_rule_id)]
|
||||
(when-not (:transaction-rule/description existing-transaction-rule)
|
||||
@@ -59,62 +58,59 @@
|
||||
(. java.util.regex.Pattern (compile description java.util.regex.Pattern/CASE_INSENSITIVE))
|
||||
(catch Exception e
|
||||
(throw (ex-info (ex-message e) {:validation-error (ex-message e)}))))
|
||||
_ (when-not (dollars= 1.0 account-total)
|
||||
_ (when-not (dollars= 1.0 account-total)
|
||||
(let [error (str "Account total (" account-total ") does not reach 100%")]
|
||||
(throw (ex-info error {:validation-error error}))))
|
||||
_ (when (and (str/blank? description)
|
||||
(nil? yodlee_merchant_id))
|
||||
(nil? yodlee_merchant_id))
|
||||
(let [error (str "You must provide a description or a yodlee merchant")]
|
||||
(throw (ex-info error {:validation-error error}))))
|
||||
_ (doseq [a accounts
|
||||
:let [{:keys [:account/location :account/name]} (dc/pull (dc/db conn) [:account/location :account/name] (:account_id a))
|
||||
client (dc/pull (dc/db conn) [:client/locations] client_id)
|
||||
]]
|
||||
client (dc/pull (dc/db conn) [:client/locations] client_id)]]
|
||||
(when (and location (not= location (:location a)))
|
||||
(let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)]
|
||||
(throw (ex-info err {:validation-error err}) )))
|
||||
|
||||
(throw (ex-info err {:validation-error err}))))
|
||||
|
||||
(when (and (not location)
|
||||
(not (get (into #{"Shared"} (:client/locations client))
|
||||
(:location a))))
|
||||
(let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")]
|
||||
(throw (ex-info err {:validation-error err}) ))))
|
||||
(throw (ex-info err {:validation-error err})))))
|
||||
rule-id (if id
|
||||
id
|
||||
"transaction-rule")
|
||||
transaction [[:upsert-entity #:transaction-rule {:db/id (or rule-id (random-tempid))
|
||||
:description description
|
||||
:note note
|
||||
:client client_id
|
||||
:bank-account bank_account_id
|
||||
:yodlee-merchant yodlee_merchant_id
|
||||
:dom-lte dom_lte
|
||||
:dom-gte dom_gte
|
||||
:amount-lte amount_lte
|
||||
:amount-gte amount_gte
|
||||
:vendor vendor_id
|
||||
:transaction-approval-status
|
||||
(some->> transaction_approval_status
|
||||
name
|
||||
snake->kebab
|
||||
(keyword "transaction-approval-status"))
|
||||
:transaction-rule/accounts (map transaction-rule-account->entity accounts)}]]
|
||||
|
||||
:description description
|
||||
:note note
|
||||
:client client_id
|
||||
:bank-account bank_account_id
|
||||
:yodlee-merchant yodlee_merchant_id
|
||||
:dom-lte dom_lte
|
||||
:dom-gte dom_gte
|
||||
:amount-lte amount_lte
|
||||
:amount-gte amount_gte
|
||||
:vendor vendor_id
|
||||
:transaction-approval-status
|
||||
(some->> transaction_approval_status
|
||||
name
|
||||
snake->kebab
|
||||
(keyword "transaction-approval-status"))
|
||||
:transaction-rule/accounts (map transaction-rule-account->entity accounts)}]]
|
||||
|
||||
transaction-result (audit-transact transaction (:id context))]
|
||||
(-> (tr/get-by-id (or (-> transaction-result :tempids (get "transaction-rule"))
|
||||
id))
|
||||
id))
|
||||
((ident->enum-f :transaction-rule/transaction-approval-status))
|
||||
(->graphql))))
|
||||
|
||||
(defn -test-transaction-rule [id {:keys [:transaction-rule/description :transaction-rule/client :transaction-rule/bank-account :transaction-rule/amount-lte :transaction-rule/amount-gte :transaction-rule/dom-lte :transaction-rule/dom-gte :transaction-rule/yodlee-merchant]} include-coded? count]
|
||||
(let [query (cond-> {:query {:find ['(pull ?e [* {:transaction/client [:client/name]
|
||||
:transaction/bank-account [:bank-account/name]
|
||||
:transaction/payment [:db/id]}
|
||||
])]
|
||||
:in ['$ ]
|
||||
:transaction/payment [:db/id]}])]
|
||||
:in ['$]
|
||||
:where []}
|
||||
:args [(dc/db conn)]}
|
||||
:args [(dc/db conn)]}
|
||||
description
|
||||
(merge-query {:query {:in ['?descr]
|
||||
:where ['[(iol-ion.query/->pattern ?descr) ?description-regex]]}
|
||||
@@ -170,23 +166,22 @@
|
||||
:where ['[?e :transaction/client ?client-id]]}
|
||||
:args [(:db/id client)]})
|
||||
|
||||
|
||||
(not include-coded?)
|
||||
(merge-query {:query {:where ['[or [?e :transaction/approval-status :transaction-approval-status/unapproved]
|
||||
[(missing? $ ?e :transaction/approval-status)]]]}})
|
||||
|
||||
true
|
||||
(merge-query {:query {:where ['[?e :transaction/id]]}}))]
|
||||
(->>
|
||||
(query2 query)
|
||||
(transduce (comp
|
||||
(take (or count 15))
|
||||
(map first)
|
||||
(map #(dissoc % :transaction/id))
|
||||
(map (fn [x]
|
||||
(update x :transaction/date c/from-date)))
|
||||
(map ->graphql))
|
||||
conj []))))
|
||||
(->>
|
||||
(query2 query)
|
||||
(transduce (comp
|
||||
(take (or count 15))
|
||||
(map first)
|
||||
(map #(dissoc % :transaction/id))
|
||||
(map (fn [x]
|
||||
(update x :transaction/date c/from-date)))
|
||||
(map ->graphql))
|
||||
conj []))))
|
||||
|
||||
(defn test-transaction-rule [{:keys [id]} {{:keys [description client_id bank_account_id amount_lte amount_gte dom_lte dom_gte yodlee_merchant_id]} :transaction_rule} _]
|
||||
(assert-admin id)
|
||||
@@ -200,7 +195,6 @@
|
||||
:yodlee-merchant (when yodlee_merchant_id {:db/id yodlee_merchant_id})}
|
||||
true 15))
|
||||
|
||||
|
||||
(defn run-transaction-rule [{:keys [id]} {:keys [transaction_rule_id count]} _]
|
||||
(assert-admin id)
|
||||
(-test-transaction-rule id (tr/get-by-id transaction_rule_id) false count))
|
||||
|
||||
@@ -66,14 +66,14 @@
|
||||
|
||||
(defn get-ids-matching-filters [args]
|
||||
(alog/info ::getting-ids-matching-filters
|
||||
:args args)
|
||||
:args args)
|
||||
(let [ids (some-> (:filters args)
|
||||
(assoc :clients (:clients args))
|
||||
(assoc :id (:id args))
|
||||
(<-graphql)
|
||||
(update :approval-status enum->keyword "transaction-approval-status")
|
||||
(assoc :per-page Integer/MAX_VALUE)
|
||||
(d-transactions/raw-graphql-ids )
|
||||
(d-transactions/raw-graphql-ids)
|
||||
:ids)
|
||||
specific-ids (d-transactions/filter-ids (seq (:ids args)))]
|
||||
(if (seq (:ids args))
|
||||
@@ -83,13 +83,13 @@
|
||||
(defn all-ids-not-locked [all-ids]
|
||||
(->> all-ids
|
||||
(dc/q '[:find ?t
|
||||
:in $ [?t ...]
|
||||
:where
|
||||
[?t :transaction/client ?c]
|
||||
[(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
|
||||
[?t :transaction/date ?d]
|
||||
[(>= ?d ?lu)]]
|
||||
(dc/db conn))
|
||||
:in $ [?t ...]
|
||||
:where
|
||||
[?t :transaction/client ?c]
|
||||
[(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
|
||||
[?t :transaction/date ?d]
|
||||
[(>= ?d ?lu)]]
|
||||
(dc/db conn))
|
||||
(map first)))
|
||||
(defn bulk-change-status [context args _]
|
||||
(let [_ (assert-admin (:id context))
|
||||
@@ -98,47 +98,46 @@
|
||||
all-ids-not-locked)]
|
||||
|
||||
(alog/info ::bulk-change-status
|
||||
:count (count all-ids)
|
||||
:sample (take 3 all-ids)
|
||||
:status (:status args)
|
||||
)
|
||||
:count (count all-ids)
|
||||
:sample (take 3 all-ids)
|
||||
:status (:status args))
|
||||
(audit-transact-batch
|
||||
(->> all-ids
|
||||
(mapv (fn [t]
|
||||
[:upsert-transaction {:db/id t
|
||||
:transaction/approval-status (enum->keyword (:status args) "transaction-approval-status")}])))
|
||||
|
||||
|
||||
(:id context))
|
||||
{:message (str "Succesfully changed " (count all-ids) " transactions to be " (name (:status args) ) ".")}))
|
||||
{:message (str "Succesfully changed " (count all-ids) " transactions to be " (name (:status args)) ".")}))
|
||||
|
||||
;; TODO very similar to rule-matching
|
||||
(defn maybe-code-accounts [transaction account-rules valid-locations]
|
||||
(with-precision 2
|
||||
(let [accounts (vec (mapcat
|
||||
(fn [ar]
|
||||
(let [cents-to-distribute (int (Math/round (Math/abs (* (:percentage ar)
|
||||
(:transaction/amount transaction)
|
||||
100))))]
|
||||
(if (= "Shared" (:location ar))
|
||||
(->> valid-locations
|
||||
(map
|
||||
(fn [cents location]
|
||||
{:db/id (random-tempid)
|
||||
:transaction-account/account (:account_id ar)
|
||||
:transaction-account/amount (* 0.01 cents)
|
||||
:transaction-account/location location})
|
||||
(rm/spread-cents cents-to-distribute (count valid-locations))))
|
||||
[(cond-> {:db/id (random-tempid)
|
||||
:transaction-account/account (:account_id ar)
|
||||
:transaction-account/amount (* 0.01 cents-to-distribute)}
|
||||
(:location ar) (assoc :transaction-account/location (:location ar)))])))
|
||||
account-rules))
|
||||
(fn [ar]
|
||||
(let [cents-to-distribute (int (Math/round (Math/abs (* (:percentage ar)
|
||||
(:transaction/amount transaction)
|
||||
100))))]
|
||||
(if (= "Shared" (:location ar))
|
||||
(->> valid-locations
|
||||
(map
|
||||
(fn [cents location]
|
||||
{:db/id (random-tempid)
|
||||
:transaction-account/account (:account_id ar)
|
||||
:transaction-account/amount (* 0.01 cents)
|
||||
:transaction-account/location location})
|
||||
(rm/spread-cents cents-to-distribute (count valid-locations))))
|
||||
[(cond-> {:db/id (random-tempid)
|
||||
:transaction-account/account (:account_id ar)
|
||||
:transaction-account/amount (* 0.01 cents-to-distribute)}
|
||||
(:location ar) (assoc :transaction-account/location (:location ar)))])))
|
||||
account-rules))
|
||||
accounts (mapv
|
||||
(fn [a]
|
||||
(update a :transaction-account/amount
|
||||
#(with-precision 2
|
||||
(double (.setScale (bigdec %) 2 java.math.RoundingMode/HALF_UP)))))
|
||||
accounts)
|
||||
(fn [a]
|
||||
(update a :transaction-account/amount
|
||||
#(with-precision 2
|
||||
(double (.setScale (bigdec %) 2 java.math.RoundingMode/HALF_UP)))))
|
||||
accounts)
|
||||
leftover (with-precision 2 (.round (bigdec (- (Math/abs (:transaction/amount transaction))
|
||||
(Math/abs (reduce + 0.0 (map #(:transaction-account/amount %) accounts)))))
|
||||
*math-context*))
|
||||
@@ -152,13 +151,13 @@
|
||||
(when-not (seq (:clients context))
|
||||
(throw (ex-info "Client is required"
|
||||
{:validation-error "Client is required"})))
|
||||
(let [args (assoc args :clients (:clients context) :id (:id context))
|
||||
(let [args (assoc args :clients (:clients context) :id (:id context))
|
||||
client->locations (->> (:clients context)
|
||||
(map :db/id )
|
||||
(map :db/id)
|
||||
(dc/q
|
||||
'[:find (pull ?e [:db/id :client/locations])
|
||||
:in $ [?e ...]]
|
||||
(dc/db conn))
|
||||
'[:find (pull ?e [:db/id :client/locations])
|
||||
:in $ [?e ...]]
|
||||
(dc/db conn))
|
||||
(map (fn [[client]]
|
||||
[(:db/id client) (:client/locations client)]))
|
||||
(into {}))
|
||||
@@ -166,41 +165,40 @@
|
||||
transactions (pull-many (dc/db conn) [:db/id :transaction/amount {:transaction/client [:db/id]}] (vec all-ids))
|
||||
account-total (reduce + 0 (map (fn [x] (:percentage x)) (:accounts args)))]
|
||||
(alog/info ::bulk-coding-transactions
|
||||
:count (count transactions)
|
||||
:sample (take 3 transactions))
|
||||
:count (count transactions)
|
||||
:sample (take 3 transactions))
|
||||
(when
|
||||
(and
|
||||
(seq (:accounts args))
|
||||
(not (dollars= 1.0 account-total)))
|
||||
(let [error (str "Account total (" account-total ") does not reach 100%")]
|
||||
(throw (ex-info error {:validation-error error}))))
|
||||
(and
|
||||
(seq (:accounts args))
|
||||
(not (dollars= 1.0 account-total)))
|
||||
(let [error (str "Account total (" account-total ") does not reach 100%")]
|
||||
(throw (ex-info error {:validation-error error}))))
|
||||
(doseq [a (:accounts args)
|
||||
:let [{:keys [:account/location :account/name]} (dc/pull (dc/db conn)
|
||||
[:account/location :account/name]
|
||||
(:account_id a))]]
|
||||
(when (and location (not= location (:location a)))
|
||||
(let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)]
|
||||
(throw (ex-info err {:validation-error err}) )))
|
||||
(throw (ex-info err {:validation-error err}))))
|
||||
(doseq [[_ locations] client->locations]
|
||||
(when (and (not location)
|
||||
(not (get (into #{"Shared"} locations)
|
||||
(:location a))))
|
||||
(let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")]
|
||||
(throw (ex-info err {:validation-error err}) )))))
|
||||
(throw (ex-info err {:validation-error err}))))))
|
||||
(audit-transact-batch
|
||||
(map (fn [t]
|
||||
(let [locations (client->locations (-> t :transaction/client :db/id))]
|
||||
(doto
|
||||
[:upsert-transaction (cond-> t
|
||||
(:approval_status args) (assoc :transaction/approval-status (enum->keyword (:approval_status args) "transaction-approval-status"))
|
||||
(:vendor args) (assoc :transaction/vendor (:vendor args))
|
||||
(seq (:accounts args)) (assoc :transaction/accounts (maybe-code-accounts t (:accounts args) locations)))]
|
||||
clojure.pprint/pprint)))
|
||||
transactions)
|
||||
(:id context))
|
||||
(map (fn [t]
|
||||
(let [locations (client->locations (-> t :transaction/client :db/id))]
|
||||
(doto
|
||||
[:upsert-transaction (cond-> t
|
||||
(:approval_status args) (assoc :transaction/approval-status (enum->keyword (:approval_status args) "transaction-approval-status"))
|
||||
(:vendor args) (assoc :transaction/vendor (:vendor args))
|
||||
(seq (:accounts args)) (assoc :transaction/accounts (maybe-code-accounts t (:accounts args) locations)))]
|
||||
clojure.pprint/pprint)))
|
||||
transactions)
|
||||
(:id context))
|
||||
{:message (str "Successfully coded " (count all-ids) " transactions.")}))
|
||||
|
||||
|
||||
(defn delete-transactions [context args _]
|
||||
(let [_ (assert-admin (:id context))
|
||||
args (assoc args :clients (:clients context))
|
||||
@@ -208,24 +206,24 @@
|
||||
db (dc/db conn)]
|
||||
|
||||
(alog/info ::bulk-delete-transactions
|
||||
:count (count all-ids)
|
||||
:sample (take 3 all-ids))
|
||||
:count (count all-ids)
|
||||
:sample (take 3 all-ids))
|
||||
(audit-transact-batch
|
||||
(mapcat (fn [i]
|
||||
(let [transaction (dc/pull db [:transaction/payment
|
||||
:transaction/expected-deposit
|
||||
:db/id] i)
|
||||
payment-id (-> transaction :transaction/payment :db/id)
|
||||
expected-deposit-id (-> transaction :transaction/expected-deposit :db/id)]
|
||||
(cond->> [[:db/retractEntity [:journal-entry/original-entity i]]]
|
||||
payment-id (into [{:db/id payment-id
|
||||
:payment/status :payment-status/pending}
|
||||
[:db/retract (:db/id transaction) :transaction/payment payment-id]])
|
||||
expected-deposit-id (into [{:db/id expected-deposit-id
|
||||
:expected-deposit/status :expected-deposit-status/pending}
|
||||
[:db/retract (:db/id transaction) :transaction/expected-deposit expected-deposit-id]]))))
|
||||
all-ids)
|
||||
(:id context))
|
||||
(mapcat (fn [i]
|
||||
(let [transaction (dc/pull db [:transaction/payment
|
||||
:transaction/expected-deposit
|
||||
:db/id] i)
|
||||
payment-id (-> transaction :transaction/payment :db/id)
|
||||
expected-deposit-id (-> transaction :transaction/expected-deposit :db/id)]
|
||||
(cond->> [[:db/retractEntity [:journal-entry/original-entity i]]]
|
||||
payment-id (into [{:db/id payment-id
|
||||
:payment/status :payment-status/pending}
|
||||
[:db/retract (:db/id transaction) :transaction/payment payment-id]])
|
||||
expected-deposit-id (into [{:db/id expected-deposit-id
|
||||
:expected-deposit/status :expected-deposit-status/pending}
|
||||
[:db/retract (:db/id transaction) :transaction/expected-deposit expected-deposit-id]]))))
|
||||
all-ids)
|
||||
(:id context))
|
||||
(audit-transact-batch
|
||||
(mapcat (fn [i]
|
||||
(let [transaction-tx (if (:suppress args)
|
||||
@@ -242,21 +240,21 @@
|
||||
(assert-power-user (:id context))
|
||||
|
||||
(let [transaction (d-transactions/get-by-id (:transaction_id args))
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction) )
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction))
|
||||
matches-set (i-transactions/match-transaction-to-unfulfilled-autopayments (:transaction/amount transaction)
|
||||
(:db/id (:transaction/client transaction)))]
|
||||
(->graphql (for [matches matches-set]
|
||||
(for [[_ invoice-id ] matches]
|
||||
(for [[_ invoice-id] matches]
|
||||
(d-invoices/get-by-id invoice-id))))))
|
||||
|
||||
(defn get-potential-unpaid-invoices-matches [context args _]
|
||||
(assert-power-user (:id context))
|
||||
(let [transaction (d-transactions/get-by-id (:transaction_id args))
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction) )
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction))
|
||||
matches-set (i-transactions/match-transaction-to-unpaid-invoices (:transaction/amount transaction)
|
||||
(:db/id (:transaction/client transaction)))]
|
||||
(->graphql (for [matches matches-set]
|
||||
(for [[_ invoice-id ] matches]
|
||||
(for [[_ invoice-id] matches]
|
||||
(d-invoices/get-by-id invoice-id))))))
|
||||
|
||||
(defn unlink-transaction [context args _]
|
||||
@@ -264,20 +262,20 @@
|
||||
args (assoc args :id (:id context))
|
||||
transaction-id (:transaction_id args)
|
||||
transaction (dc/pull (dc/db conn)
|
||||
[:transaction/approval-status
|
||||
:transaction/status
|
||||
:transaction/date
|
||||
:transaction/location
|
||||
:transaction/vendor
|
||||
:transaction/accounts
|
||||
:transaction/client [:db/id]
|
||||
{:transaction/payment [:payment/date {:payment/status [:db/ident]} :db/id]} ]
|
||||
transaction-id)
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction) )
|
||||
[:transaction/approval-status
|
||||
:transaction/status
|
||||
:transaction/date
|
||||
:transaction/location
|
||||
:transaction/vendor
|
||||
:transaction/accounts
|
||||
:transaction/client [:db/id]
|
||||
{:transaction/payment [:payment/date {:payment/status [:db/ident]} :db/id]}]
|
||||
transaction-id)
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction))
|
||||
_ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction))
|
||||
_ (when (:transaction/payment transaction)
|
||||
(assert-not-locked (:db/id (:transaction/client transaction)) (-> transaction :transaction/payment :payment/date)))
|
||||
payment (-> transaction :transaction/payment )
|
||||
payment (-> transaction :transaction/payment)
|
||||
is-autopay-payment? (some->> (dc/q {:find ['?sp]
|
||||
:in ['$ '?payment]
|
||||
:where ['[?ip :invoice-payment/payment ?payment]
|
||||
@@ -286,8 +284,7 @@
|
||||
(dc/db conn) (:db/id payment))
|
||||
seq
|
||||
(map first)
|
||||
(every? #(instance? java.util.Date %)))
|
||||
]
|
||||
(every? #(instance? java.util.Date %)))]
|
||||
|
||||
(alog/info ::unlinking :transaction (pr-str transaction) :autopay is-autopay-payment? :payment (pr-str payment))
|
||||
|
||||
@@ -295,49 +292,47 @@
|
||||
(throw (ex-info "Payment can't be undone because it isn't cleared." {:validation-error "Payment can't be undone because it isn't cleared."})))
|
||||
(if is-autopay-payment?
|
||||
(audit-transact
|
||||
(-> [{:db/id (:db/id payment)
|
||||
:payment/status :payment-status/pending}
|
||||
[:upsert-transaction
|
||||
{:db/id transaction-id
|
||||
:transaction/approval-status :transaction-approval-status/unapproved
|
||||
:transaction/payment nil
|
||||
:transaction/vendor nil
|
||||
:transaction/location nil
|
||||
:transaction/accounts nil}]
|
||||
(-> [{:db/id (:db/id payment)
|
||||
:payment/status :payment-status/pending}
|
||||
[:upsert-transaction
|
||||
{:db/id transaction-id
|
||||
:transaction/approval-status :transaction-approval-status/unapproved
|
||||
:transaction/payment nil
|
||||
:transaction/vendor nil
|
||||
:transaction/location nil
|
||||
:transaction/accounts nil}]
|
||||
|
||||
[:db/retractEntity (:db/id payment) ]]
|
||||
[:db/retractEntity (:db/id payment)]]
|
||||
|
||||
(into (map (fn [[invoice-payment]]
|
||||
[:db/retractEntity invoice-payment])
|
||||
(dc/q {:find ['?ip]
|
||||
:in ['$ '?p]
|
||||
:where ['[?ip :invoice-payment/payment ?p]]}
|
||||
(dc/db conn)
|
||||
(:db/id payment) ))))
|
||||
(into (map (fn [[invoice-payment]]
|
||||
[:db/retractEntity invoice-payment])
|
||||
(dc/q {:find ['?ip]
|
||||
:in ['$ '?p]
|
||||
:where ['[?ip :invoice-payment/payment ?p]]}
|
||||
(dc/db conn)
|
||||
(:db/id payment)))))
|
||||
(:id context))
|
||||
(audit-transact
|
||||
[{:db/id (:db/id payment)
|
||||
:payment/status :payment-status/pending}
|
||||
[:upsert-transaction
|
||||
{:db/id transaction-id
|
||||
:transaction/approval-status :transaction-approval-status/unapproved
|
||||
:transaction/payment nil
|
||||
:transaction/vendor nil
|
||||
:transaction/location nil
|
||||
:transaction/accounts nil}]]
|
||||
[{:db/id (:db/id payment)
|
||||
:payment/status :payment-status/pending}
|
||||
[:upsert-transaction
|
||||
{:db/id transaction-id
|
||||
:transaction/approval-status :transaction-approval-status/unapproved
|
||||
:transaction/payment nil
|
||||
:transaction/vendor nil
|
||||
:transaction/location nil
|
||||
:transaction/accounts nil}]]
|
||||
(:id context)))
|
||||
(-> (d-transactions/get-by-id transaction-id)
|
||||
approval-status->graphql
|
||||
->graphql)))
|
||||
|
||||
|
||||
(defn transaction-account->entity [{:keys [id account_id amount location]}]
|
||||
#:transaction-account {:amount amount
|
||||
:db/id (or id (random-tempid))
|
||||
:account account_id
|
||||
:location location})
|
||||
|
||||
|
||||
(defn assert-valid-expense-accounts [accounts]
|
||||
(doseq [trans-account accounts
|
||||
:let [account (dc/pull (dc/db conn)
|
||||
@@ -351,7 +346,7 @@
|
||||
(:account/location account)))
|
||||
(let [err (str "Account uses location '" (:location trans-account) "' but expects '" (:account/location account) "'")]
|
||||
(throw (ex-info err
|
||||
{:validation-error err}))))
|
||||
{:validation-error err}))))
|
||||
|
||||
(when (and (empty? (:account/location account))
|
||||
(= "A" (:location trans-account)))
|
||||
@@ -359,13 +354,12 @@
|
||||
(throw (ex-info err
|
||||
{:validation-error err}))))
|
||||
|
||||
|
||||
(when (nil? (:account_id trans-account))
|
||||
(throw (ex-info "Account is missing account" {:validation-error "Account is missing account"})))))
|
||||
|
||||
(defn edit-transaction [context {{:keys [id accounts vendor_id approval_status memo forecast_match]} :transaction} _]
|
||||
(let [existing-transaction (d-transactions/get-by-id id)
|
||||
_ (assert-can-see-client (:id context) (:transaction/client existing-transaction) )
|
||||
_ (assert-can-see-client (:id context) (:transaction/client existing-transaction))
|
||||
_ (assert-valid-expense-accounts accounts)
|
||||
_ (assert-not-locked (:db/id (:transaction/client existing-transaction)) (:transaction/date existing-transaction))
|
||||
account-total (reduce + 0 (map (fn [x] (:amount x)) accounts))
|
||||
@@ -378,17 +372,17 @@
|
||||
set
|
||||
(conj "A")
|
||||
(conj "HQ"))))]
|
||||
|
||||
|
||||
(when (and (not (dollars= (Math/abs (:transaction/amount existing-transaction)) account-total))
|
||||
(or
|
||||
(and (= approval_status :unapproved)
|
||||
(> (count accounts) 0))
|
||||
(not= approval_status :unapproved)))
|
||||
(not= approval_status :unapproved)))
|
||||
(let [error (str "Expense account total (" account-total ") does not equal transaction total (" (Math/abs (:transaction/amount existing-transaction)) ")")]
|
||||
(throw (ex-info error {:validation-error error}))))
|
||||
(throw (ex-info error {:validation-error error}))))
|
||||
(when missing-locations
|
||||
(throw (ex-info (str "Location '" (str/join ", " missing-locations) "' not found on client.") {})) )
|
||||
|
||||
(throw (ex-info (str "Location '" (str/join ", " missing-locations) "' not found on client.") {})))
|
||||
|
||||
(audit-transact (cond-> [[:upsert-transaction {:db/id id
|
||||
:transaction/vendor vendor_id
|
||||
:transaction/memo memo
|
||||
@@ -413,8 +407,8 @@
|
||||
(defn match-transaction [context {:keys [transaction_id payment_id]} _]
|
||||
(let [transaction (d-transactions/get-by-id transaction_id)
|
||||
payment (d-checks/get-by-id payment_id)
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction) )
|
||||
_ (assert-can-see-client (:id context) (:payment/client payment) )
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction))
|
||||
_ (assert-can-see-client (:id context) (:payment/client payment))
|
||||
_ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction))]
|
||||
(when (not= (:db/id (:transaction/client transaction))
|
||||
(:db/id (:payment/client payment)))
|
||||
@@ -423,7 +417,7 @@
|
||||
(when-not (dollars= (- (:transaction/amount transaction))
|
||||
(:payment/amount payment))
|
||||
(throw (ex-info "Amounts don't match" {:validation-error "Amounts don't match"})))
|
||||
(audit-transact (into
|
||||
(audit-transact (into
|
||||
[{:db/id (:db/id payment)
|
||||
:payment/status :payment-status/cleared
|
||||
:payment/date (coerce/to-date (first (sort [(:payment/date payment)
|
||||
@@ -431,14 +425,14 @@
|
||||
|
||||
[:upsert-transaction
|
||||
{:db/id (:db/id transaction)
|
||||
:transaction/payment (:db/id payment)
|
||||
:transaction/vendor (:db/id (:payment/vendor payment))
|
||||
:transaction/location "A"
|
||||
:transaction/approval-status :transaction-approval-status/approved
|
||||
:transaction/accounts [{:db/id (random-tempid)
|
||||
:transaction-account/account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"]))
|
||||
:transaction-account/location "A"
|
||||
:transaction-account/amount (Math/abs (:transaction/amount transaction))}]}]])
|
||||
:transaction/payment (:db/id payment)
|
||||
:transaction/vendor (:db/id (:payment/vendor payment))
|
||||
:transaction/location "A"
|
||||
:transaction/approval-status :transaction-approval-status/approved
|
||||
:transaction/accounts [{:db/id (random-tempid)
|
||||
:transaction-account/account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"]))
|
||||
:transaction-account/location "A"
|
||||
:transaction-account/amount (Math/abs (:transaction/amount transaction))}]}]])
|
||||
(:id context)))
|
||||
(solr/touch-with-ledger transaction_id)
|
||||
(-> (d-transactions/get-by-id transaction_id)
|
||||
@@ -448,7 +442,7 @@
|
||||
(defn match-transaction-autopay-invoices [context {:keys [transaction_id autopay_invoice_ids]} _]
|
||||
(let [_ (assert-power-user (:id context))
|
||||
transaction (d-transactions/get-by-id transaction_id)
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction) )
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction))
|
||||
db (dc/db conn)
|
||||
invoice-clients (set (map #(pull-ref db :invoice/client %) autopay_invoice_ids))
|
||||
invoice-amount (reduce + 0.0 (map #(pull-attr db :invoice/total %) autopay_invoice_ids))
|
||||
@@ -474,9 +468,9 @@
|
||||
(:db/id (:transaction/bank-account transaction))
|
||||
(:db/id (:transaction/client transaction)))]
|
||||
(alog/info ::adding-payment-from-autopay-invoice
|
||||
:payment (pr-str payment-tx))
|
||||
:payment (pr-str payment-tx))
|
||||
(audit-transact payment-tx (:id context)))
|
||||
(solr/touch-with-ledger transaction_id)
|
||||
(solr/touch-with-ledger transaction_id)
|
||||
(-> (d-transactions/get-by-id transaction_id)
|
||||
approval-status->graphql
|
||||
->graphql)))
|
||||
@@ -485,8 +479,8 @@
|
||||
(defn match-transaction-unpaid-invoices [context {:keys [transaction_id unpaid_invoice_ids]} _]
|
||||
(let [_ (assert-power-user (:id context))
|
||||
transaction (d-transactions/get-by-id transaction_id)
|
||||
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction) )
|
||||
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction))
|
||||
_ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction))
|
||||
db (dc/db conn)
|
||||
invoice-clients (set (map #(pull-ref db :invoice/client %) unpaid_invoice_ids))
|
||||
@@ -502,17 +496,17 @@
|
||||
(throw (ex-info "Amounts don't match" {:validation-error "Amounts don't match"})))
|
||||
(when (:transaction/payment transaction)
|
||||
(throw (ex-info "Transaction already linked" {:validation-error "Transaction already linked"})))
|
||||
|
||||
|
||||
(let [payment-tx (i-transactions/add-new-payment (dc/pull db [:transaction/amount :transaction/date :db/id] transaction_id)
|
||||
(map (fn [id]
|
||||
(let [entity (dc/pull db [:invoice/vendor :db/id :invoice/total] id)]
|
||||
[(or (-> entity :invoice/vendor :db/id)
|
||||
(-> entity :invoice/vendor))
|
||||
(-> entity :db/id)
|
||||
(-> entity :invoice/total)]))
|
||||
unpaid_invoice_ids)
|
||||
(:db/id (:transaction/bank-account transaction))
|
||||
(:db/id (:transaction/client transaction)))]
|
||||
(map (fn [id]
|
||||
(let [entity (dc/pull db [:invoice/vendor :db/id :invoice/total] id)]
|
||||
[(or (-> entity :invoice/vendor :db/id)
|
||||
(-> entity :invoice/vendor))
|
||||
(-> entity :db/id)
|
||||
(-> entity :invoice/total)]))
|
||||
unpaid_invoice_ids)
|
||||
(:db/id (:transaction/bank-account transaction))
|
||||
(:db/id (:transaction/client transaction)))]
|
||||
(audit-transact payment-tx (:id context)))
|
||||
(solr/touch-with-ledger transaction_id)
|
||||
|
||||
@@ -527,9 +521,8 @@
|
||||
:count Integer/MAX_VALUE} nil)
|
||||
|
||||
(filter #(not (:payment %)))
|
||||
(map :id ))
|
||||
(map :id))
|
||||
|
||||
|
||||
transaction_ids)
|
||||
_ (mu/log ::here :txids transaction_ids)
|
||||
transaction_ids (all-ids-not-locked transaction_ids)
|
||||
@@ -553,17 +546,16 @@
|
||||
(audit-transact (mapv (fn [t]
|
||||
[:upsert-transaction
|
||||
(remove-nils (rm/apply-rule {:db/id (:db/id t)
|
||||
:transaction/amount (:transaction/amount t)}
|
||||
transaction-rule
|
||||
:transaction/amount (:transaction/amount t)}
|
||||
transaction-rule
|
||||
|
||||
(or (-> t :transaction/bank-account :bank-account/locations)
|
||||
(-> t :transaction/client :client/locations))))])
|
||||
(or (-> t :transaction/bank-account :bank-account/locations)
|
||||
(-> t :transaction/client :client/locations))))])
|
||||
transactions)
|
||||
(:id context))
|
||||
|
||||
(doseq [n transactions]
|
||||
(solr/touch-with-ledger (:db/id n)))
|
||||
)
|
||||
(solr/touch-with-ledger (:db/id n))))
|
||||
(transduce
|
||||
(comp
|
||||
(map d-transactions/get-by-id)
|
||||
@@ -571,12 +563,12 @@
|
||||
(map ->graphql))
|
||||
conj
|
||||
[]
|
||||
transaction_ids ))
|
||||
transaction_ids))
|
||||
|
||||
(def objects
|
||||
{:transaction {:fields {:id {:type :id}
|
||||
:amount {:type 'String}
|
||||
:memo {:type 'String}
|
||||
:memo {:type 'String}
|
||||
:is_locked {:type 'Boolean}
|
||||
:description_original {:type 'String}
|
||||
:description_simple {:type 'String}
|
||||
@@ -628,8 +620,8 @@
|
||||
:resolve :mutation/bulk-code-transactions}
|
||||
:delete_transactions {:type :message
|
||||
:args {:filters {:type :transaction_filters}
|
||||
:ids {:type '(list :id)}
|
||||
:suppress {:type 'Boolean}}
|
||||
:ids {:type '(list :id)}
|
||||
:suppress {:type 'Boolean}}
|
||||
:resolve :mutation/delete-transactions}
|
||||
:edit_transaction {:type :transaction
|
||||
:args {:transaction {:type :edit_transaction}}
|
||||
@@ -711,9 +703,8 @@
|
||||
:mutation/match-transaction-unpaid-invoices match-transaction-unpaid-invoices
|
||||
:mutation/match-transaction-rules match-transaction-rules})
|
||||
|
||||
|
||||
(defn attach [schema]
|
||||
(->
|
||||
(->
|
||||
(merge-with merge schema
|
||||
{:objects objects
|
||||
:queries queries
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
[iol-ion.query :refer [entid]]
|
||||
[slingshot.slingshot :refer [throw+]]))
|
||||
|
||||
|
||||
(defn snake->kebab [s]
|
||||
(str/replace s #"_" "-"))
|
||||
|
||||
@@ -107,8 +106,6 @@
|
||||
(#{"manager" "user" "power-user" "read-only"} (:user/role id))
|
||||
(:user/clients id [])))
|
||||
|
||||
|
||||
|
||||
(defn result->page [results result-count key args]
|
||||
{key (map ->graphql results)
|
||||
:total result-count
|
||||
@@ -197,7 +194,6 @@
|
||||
(= :client/code (first x)))
|
||||
[(entid (dc/db conn) x)]
|
||||
|
||||
|
||||
(sequential? x)
|
||||
(mapcat coerce-client-ids x)
|
||||
|
||||
@@ -218,14 +214,14 @@
|
||||
e)))))
|
||||
|
||||
(defn exception->4xx [f]
|
||||
(try
|
||||
(try
|
||||
(f)
|
||||
(catch Throwable e
|
||||
(throw+ (ex-info (.getMessage e) {:type :form-validation
|
||||
:form-validation-errors [(.getMessage e)]}))
|
||||
(throw+ (ex-info (.getMessage e) {:type :form-validation
|
||||
:form-validation-errors [(.getMessage e)]}))
|
||||
#_(throw (ex-info (.getMessage e)
|
||||
{:type :notification}
|
||||
e)))))
|
||||
{:type :notification}
|
||||
e)))))
|
||||
|
||||
(defn notify-if-locked [client-id date]
|
||||
(try
|
||||
|
||||
@@ -130,7 +130,6 @@
|
||||
:vendor/schedule-payment-dom schedule-payment-dom
|
||||
:vendor/automatically-paid-when-due (:automatically_paid_when_due in)))]
|
||||
|
||||
|
||||
transaction-result (audit-transact [transaction] (:id context))
|
||||
new-vendor (d-vendors/get-by-id (or (-> transaction-result :tempids (get "vendor"))
|
||||
id))]
|
||||
@@ -160,7 +159,6 @@
|
||||
(audit-transact transaction (:id context))
|
||||
to))
|
||||
|
||||
|
||||
(defn get-graphql [context args _]
|
||||
(assert-admin (:id context))
|
||||
(let [args (assoc args :id (:id context))
|
||||
@@ -187,7 +185,6 @@
|
||||
(if-let [query (not-empty (cleanse-query (:query args)))]
|
||||
(let [search-query (str "name:(" query ")")]
|
||||
|
||||
|
||||
(for [{:keys [id name]} (solr/query solr/impl "vendors" {"query" (cond-> search-query
|
||||
(not (is-admin? (:id context))) (str " hidden:false"))
|
||||
"fields" "id, name"})]
|
||||
|
||||
Reference in New Issue
Block a user