First cut at bulk coding
This commit is contained in:
@@ -1,32 +1,31 @@
|
||||
(ns auto-ap.graphql.transactions
|
||||
(:require [auto-ap.datomic
|
||||
:refer
|
||||
[audit-transact audit-transact-batch conn remove-nils]]
|
||||
[auto-ap.datomic.accounts :as a]
|
||||
[auto-ap.datomic.checks :as d-checks]
|
||||
[auto-ap.datomic.invoices :as d-invoices]
|
||||
[auto-ap.datomic.transaction-rules :as tr]
|
||||
[auto-ap.datomic.transactions :as d-transactions]
|
||||
[auto-ap.graphql.transaction-rules :as g-tr]
|
||||
[auto-ap.graphql.utils
|
||||
:refer
|
||||
[->graphql
|
||||
<-graphql
|
||||
assert-admin
|
||||
assert-can-see-client
|
||||
assert-power-user
|
||||
enum->keyword
|
||||
ident->enum-f
|
||||
snake->kebab]]
|
||||
[auto-ap.import.transactions :as i-transactions]
|
||||
[auto-ap.rule-matching :as rm]
|
||||
[auto-ap.utils :refer [dollars=]]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as str]
|
||||
[clojure.tools.logging :as log]
|
||||
[com.walmartlabs.lacinia.util :refer [attach-resolvers]]
|
||||
[datomic.api :as d]))
|
||||
(:require
|
||||
[auto-ap.datomic
|
||||
:refer [audit-transact audit-transact-batch conn remove-nils]]
|
||||
[auto-ap.datomic.accounts :as a]
|
||||
[auto-ap.datomic.checks :as d-checks]
|
||||
[auto-ap.datomic.invoices :as d-invoices]
|
||||
[auto-ap.datomic.transaction-rules :as tr]
|
||||
[auto-ap.datomic.transactions :as d-transactions]
|
||||
[auto-ap.graphql.transaction-rules :as g-tr]
|
||||
[auto-ap.graphql.utils
|
||||
:refer [->graphql
|
||||
<-graphql
|
||||
assert-admin
|
||||
assert-can-see-client
|
||||
assert-power-user
|
||||
enum->keyword
|
||||
ident->enum-f
|
||||
snake->kebab]]
|
||||
[auto-ap.import.transactions :as i-transactions]
|
||||
[auto-ap.rule-matching :as rm]
|
||||
[auto-ap.utils :refer [dollars=]]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as str]
|
||||
[clojure.tools.logging :as log]
|
||||
[com.walmartlabs.lacinia.util :refer [attach-resolvers]]
|
||||
[datomic.api :as d]))
|
||||
|
||||
(def approval-status->graphql (ident->enum-f :transaction/approval-status))
|
||||
|
||||
@@ -37,7 +36,7 @@
|
||||
(throw (ex-info "In order to select potential duplicates, you must choose a bank account."
|
||||
{:validation-error "In order to select potential duplicates, you must choose a bank account."})))
|
||||
(when-not (seq (->> filters
|
||||
(filter (fn [[k v]]
|
||||
(filter (fn [[_ v]]
|
||||
(not (nil? v))))
|
||||
(filter (comp (complement #{:id :start :sort :client_id :bank_account_id :potential_duplicates :per_page})
|
||||
first))
|
||||
@@ -49,7 +48,7 @@
|
||||
(throw (ex-info "In order to select potential duplicates, you must filter your view more."
|
||||
{:validation-error "In order to select potential duplicates, you must filter your view more."})))))
|
||||
|
||||
(defn get-transaction-page [context args value]
|
||||
(defn get-transaction-page [context args _]
|
||||
(let [args (assoc (:filters args) :id (:id context))
|
||||
_ (assert-filtered-enough args)
|
||||
[transactions transactions-count] (d-transactions/get-graphql (update (<-graphql args) :approval-status enum->keyword "transaction-approval-status"))
|
||||
@@ -60,18 +59,20 @@
|
||||
:start (:start args 0)
|
||||
:end (+ (:start args 0) (count transactions))}))
|
||||
|
||||
(defn bulk-change-status [context args value]
|
||||
(defn get-ids-matching-filters [args]
|
||||
(let [ids (some-> (:filters args)
|
||||
(<-graphql)
|
||||
(update :approval-status enum->keyword "transaction-approval-status")
|
||||
(assoc :per-page Integer/MAX_VALUE)
|
||||
(d-transactions/raw-graphql-ids )
|
||||
:ids)
|
||||
specific-ids (d-transactions/filter-ids (:ids args))]
|
||||
(into (set ids) specific-ids)))
|
||||
|
||||
(defn bulk-change-status [context args _]
|
||||
(let [_ (assert-admin (:id context))
|
||||
args (assoc args :id (:id context))
|
||||
ids (some-> (:filters args)
|
||||
(assoc :id (:id context))
|
||||
(<-graphql)
|
||||
(update :approval-status enum->keyword "transaction-approval-status")
|
||||
(assoc :per-page Integer/MAX_VALUE)
|
||||
(d-transactions/raw-graphql-ids )
|
||||
:ids)
|
||||
specific-ids (d-transactions/filter-ids (:ids args))
|
||||
all-ids (into (set ids) specific-ids)]
|
||||
all-ids (get-ids-matching-filters args)]
|
||||
|
||||
(log/info "Unapproving " (count all-ids) args)
|
||||
|
||||
@@ -83,18 +84,68 @@
|
||||
(:id context))
|
||||
{: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]
|
||||
(println ar)
|
||||
(let [cents-to-distribute (int (Math/round (Math/abs (* (:percentage ar)
|
||||
(:transaction/amount transaction)
|
||||
100))))]
|
||||
(if (= "Shared" (:location ar))
|
||||
(do
|
||||
(log/info "here" valid-locations)
|
||||
(->> valid-locations
|
||||
(map
|
||||
(fn [cents location]
|
||||
{: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-> {: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)
|
||||
leftover (with-precision 2 (.round (bigdec (- (Math/abs (:transaction/amount transaction))
|
||||
(Math/abs (reduce + 0.0 (map #(:transaction-account/amount %) accounts)))))
|
||||
*math-context*))
|
||||
accounts (if (seq accounts)
|
||||
(update-in accounts [(dec (count accounts)) :transaction-account/amount] #(+ % (double leftover)))
|
||||
[])]
|
||||
[:reset (:db/id transaction) :transaction/accounts accounts])))
|
||||
|
||||
(defn delete-transactions [context args value]
|
||||
(defn bulk-code-transactions [context args _]
|
||||
(assert-admin (:id context))
|
||||
(let [args (assoc args :id (:id context))
|
||||
locations (:client/locations (d/pull (d/db conn)
|
||||
[:client/locations]
|
||||
(:client_id (:filters args))))
|
||||
all-ids (get-ids-matching-filters args)
|
||||
transactions (d/pull-many (d/db conn) '[:db/id :transaction/amount] (vec all-ids))]
|
||||
(log/info "Bulk coding " (count all-ids) args)
|
||||
(audit-transact-batch
|
||||
(mapcat (fn [i]
|
||||
(cond-> [(cond-> i
|
||||
(: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)) (conj (maybe-code-accounts i (:accounts args) locations))))
|
||||
transactions)
|
||||
(:id context))
|
||||
{:message (str "Successfully coded " (count all-ids) " transactions.")}))
|
||||
|
||||
|
||||
(defn delete-transactions [context args _]
|
||||
(let [_ (assert-admin (:id context))
|
||||
args (assoc args :id (:id context))
|
||||
ids (some-> (:filters args)
|
||||
(<-graphql)
|
||||
(update :approval-status enum->keyword "transaction-approval-status")
|
||||
(assoc :per-page Integer/MAX_VALUE)
|
||||
(d-transactions/raw-graphql-ids )
|
||||
:ids)
|
||||
specific-ids (d-transactions/filter-ids (:ids args))
|
||||
all-ids (into (set ids) specific-ids)
|
||||
all-ids (get-ids-matching-filters args)
|
||||
db (d/db conn)]
|
||||
|
||||
(log/info "Deleting " (count all-ids) args)
|
||||
@@ -119,32 +170,29 @@
|
||||
(:id context))
|
||||
{:message (str "Succesfully deleted " (count all-ids) " transactions.")}))
|
||||
|
||||
(defn get-potential-autopay-invoices-matches [context args value]
|
||||
(defn get-potential-autopay-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) )]
|
||||
|
||||
(let [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]
|
||||
(d-invoices/get-by-id invoice-id)))))))
|
||||
_ (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]
|
||||
(d-invoices/get-by-id invoice-id))))))
|
||||
|
||||
(defn get-potential-unpaid-invoices-matches [context args value]
|
||||
(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) )]
|
||||
|
||||
(let [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]
|
||||
(d-invoices/get-by-id invoice-id)))))))
|
||||
_ (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]
|
||||
(d-invoices/get-by-id invoice-id))))))
|
||||
|
||||
(defn unlink-transaction [context args value]
|
||||
(defn unlink-transaction [context args _]
|
||||
(let [_ (assert-power-user (:id context))
|
||||
|
||||
args (assoc args :id (:id context))
|
||||
transaction-id (:transaction_id args)
|
||||
transaction (d/pull (d/db conn)
|
||||
@@ -154,7 +202,6 @@
|
||||
:transaction/vendor
|
||||
:transaction/accounts
|
||||
:transaction/client [:db/id]
|
||||
|
||||
{:transaction/payment [{:payment/status [:db/ident]} :db/id]} ]
|
||||
transaction-id)
|
||||
_ (assert-can-see-client (:id context) (:transaction/client transaction) )
|
||||
@@ -241,7 +288,7 @@
|
||||
(when (empty? (:location trans-account))
|
||||
(throw (ex-info "Account is missing location" {:validation-error "Account is missing location"})))
|
||||
|
||||
(when (and (not (empty? (:account/location account)))
|
||||
(when (and (seq (:account/location account))
|
||||
(not= (:location trans-account)
|
||||
(:account/location account)))
|
||||
(let [err (str "Account uses location '" (:location trans-account) "' but expects '" (:account/location account) "'")]
|
||||
@@ -258,7 +305,7 @@
|
||||
(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 forecast_match] :as transaction} :transaction} value]
|
||||
(defn edit-transaction [context {{:keys [id accounts vendor_id approval_status forecast_match]} :transaction} _]
|
||||
(let [existing-transaction (d-transactions/get-by-id id)
|
||||
_ (assert-can-see-client (:id context) (:transaction/client existing-transaction) )
|
||||
_ (assert-valid-expense-accounts accounts)
|
||||
@@ -306,7 +353,7 @@
|
||||
approval-status->graphql
|
||||
->graphql)))
|
||||
|
||||
(defn match-transaction [context {:keys [transaction_id payment_id]} value]
|
||||
(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) )
|
||||
@@ -339,7 +386,7 @@
|
||||
approval-status->graphql
|
||||
->graphql))
|
||||
|
||||
(defn match-transaction-autopay-invoices [context {:keys [transaction_id autopay_invoice_ids]} value]
|
||||
(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) )
|
||||
@@ -379,7 +426,7 @@
|
||||
approval-status->graphql
|
||||
->graphql)))
|
||||
|
||||
(defn match-transaction-unpaid-invoices [context {:keys [transaction_id unpaid_invoice_ids]} value]
|
||||
(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)
|
||||
|
||||
@@ -415,7 +462,7 @@
|
||||
approval-status->graphql
|
||||
->graphql)))
|
||||
|
||||
(defn match-transaction-rules [context {:keys [transaction_ids transaction_rule_id all]} value]
|
||||
(defn match-transaction-rules [context {:keys [transaction_ids transaction_rule_id all]} _]
|
||||
(let [_ (assert-admin (:id context))
|
||||
transaction_ids (if all
|
||||
(->> (g-tr/run-transaction-rule context {:transaction_rule_id transaction_rule_id
|
||||
@@ -509,8 +556,15 @@
|
||||
:status {:type :transaction_approval_status}
|
||||
:ids {:type '(list :id)}}
|
||||
:resolve :mutation/bulk-change-transaction-status}
|
||||
:delete_transactions {:type :message
|
||||
:args {:filters {:type :transaction_filters}
|
||||
:bulk_code_transactions {:type :message
|
||||
:args {:filters {:type :transaction_filters}
|
||||
:vendor {:type :id}
|
||||
:approval_status {:type :transaction_approval_status}
|
||||
:accounts {:type '(list :edit_percentage_account)}
|
||||
:ids {:type '(list :id)}}
|
||||
:resolve :mutation/bulk-code-transactions}
|
||||
:delete_transactions {:type :message
|
||||
:args {:filters {:type :transaction_filters}
|
||||
:ids {:type '(list :id)}
|
||||
:suppress {:type 'Boolean}}
|
||||
:resolve :mutation/delete-transactions}
|
||||
@@ -583,6 +637,7 @@
|
||||
:mutation/unlink-transaction unlink-transaction
|
||||
:mutation/bulk-change-transaction-status bulk-change-status
|
||||
:mutation/delete-transactions delete-transactions
|
||||
:mutation/bulk-code-transactions bulk-code-transactions
|
||||
:mutation/match-transaction match-transaction
|
||||
:mutation/match-transaction-autopay-invoices match-transaction-autopay-invoices
|
||||
:mutation/match-transaction-unpaid-invoices match-transaction-unpaid-invoices
|
||||
|
||||
Reference in New Issue
Block a user